7.1.1.4.3 : Le main_precompute_modulo.cpp
Le problème de notre implémentation précédente est que l'on calcule toujours le modulo afin de déterminer les données de calibration à utiliser, ce qui est un peu dommage.
On commence avec les includes standards :
1 2 3 4 5 6 7 8 9 |
#include <iostream> #include <vector> #include <numeric> // #include <ranges> #include <execution> #include <algorithm> #include "micro_benchmark.h" |
On peut définir des types pour nos données, qui permettrons de passer facilement à des données alignées ou non alignées :
1 2 3 4 |
///Defines a vector of index typedef std::vector<int> VecIndex; ///Defines a vector of data typedef std::vector<float> VecData; |
On définit notre kernel de calcul :
1 2 3 4 5 6 7 8 9 10 |
///Compute the calibration /** @param[out] vecCalibSignal : vector of calibrated signal * @param vecADC : vector of ADC values * @param tabPed : pointer to the table of pedestal * @param tabGain : pointer to the table of gain * @param vecIdx : vector of index to be used to round robin over the pixel */ void compute_calibration(VecData & vecCalibSignal, const VecData & vecADC, const float * tabPed, const float * tabGain, const VecIndex & vecIdx) { |
Comme précédemment le std::transform pourrait être remplacer par un std::for_each mais ça ne serait plus autant optimisé pour l'analyse CPU. Dans tous les cas, il faut faut pas oublier le = pour récupérer les variables extérieures à la lamba par copie. Et comme ces variables sont copiées, nous utilisons des pointeurs pour que leur copie de coûte pas cher :
1 2 3 4 5 |
std::transform(std::execution::par_unseq, std::begin(vecADC), std::end(vecADC), std::begin(vecIdx), std::begin(vecCalibSignal), [=](float adc, int i){ return (adc - tabPed[i])*tabGain[i]; }); } |
La fonction qui évalue la performance de notre kernel :
1 2 3 4 |
///Get the number of nanoseconds per elements of the Calibration /** @param nbEvent : number of events of the tables */ void evaluateCalibration(size_t nbEvent){ |
On commencer par définir quelques tailles :
1 2 3 |
//Let's define size of data : size_t nbPixel(NB_PIXEL), nbSlice(NB_SLICE); size_t nbElement(nbEvent*nbSlice*nbPixel); |
On alloue et on initialise les données de calibration :
1 2 3 4 |
VecData vecGain(nbPixel), vecPedestal(nbPixel); std::fill(vecGain.begin(), vecGain.end(), 0.02f); std::fill(vecPedestal.begin(), vecPedestal.end(), 40.0f); |
On alloue et on initialise les données à calibrer et le résultat :
1 2 3 |
VecData vecADCSignal(nbElement), vecCalibSignal(nbElement); std::fill(vecADCSignal.begin(), vecADCSignal.end(), 42.0f); |
On alloue et on initialise le tableau d'indices en calculant le modulo tout de suite :
1 2 3 4 5 6 7 8 |
VecIndex vecIdx(nbElement); //Init vector of index for the computation std::iota(vecIdx.begin(), vecIdx.end(), 0); //Hope some day views will work to avoid allocation of index vector std::transform(std::execution::par_unseq, vecIdx.begin(), vecIdx.end(), vecIdx.begin(), [=](int i){ //Modulo computation, only once at the initialisation time return i % nbPixel; } ); |
On créé les pointeurs que l(on utilisera pour notre calcul :
1 2 |
float *tabGain = vecGain.data(), *tabPed = vecPedestal.data(); |
On évalue la performance du kernel :
1 2 3 |
size_t fullNbElement(nbElement); micro_benchmarkAutoNsPrint("evaluateCalibration nvc++ pre computed modulo", fullNbElement, compute_calibration, vecCalibSignal, vecADCSignal, tabPed, tabGain, vecIdx); } |
Enfin, nous appellons la fonction d'évaluation de MicroBenchmark :
1 2 3 |
int main(int argc, char** argv){ return micro_benchmarkParseArg(argc, argv, evaluateCalibration); } |
Le fichier main_precompute_modulo.cpp complet :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
/*************************************** Auteur : Pierre Aubert Mail : pierre.aubert@lapp.in2p3.fr Licence : CeCILL-C ****************************************/ #include <iostream> #include <vector> #include <numeric> // #include <ranges> #include <execution> #include <algorithm> #include "micro_benchmark.h" ///Defines a vector of index typedef std::vector<int> VecIndex; ///Defines a vector of data typedef std::vector<float> VecData; ///Compute the calibration /** @param[out] vecCalibSignal : vector of calibrated signal * @param vecADC : vector of ADC values * @param tabPed : pointer to the table of pedestal * @param tabGain : pointer to the table of gain * @param vecIdx : vector of index to be used to round robin over the pixel */ void compute_calibration(VecData & vecCalibSignal, const VecData & vecADC, const float * tabPed, const float * tabGain, const VecIndex & vecIdx) { std::transform(std::execution::par_unseq, std::begin(vecADC), std::end(vecADC), std::begin(vecIdx), std::begin(vecCalibSignal), [=](float adc, int i){ return (adc - tabPed[i])*tabGain[i]; }); } ///Get the number of nanoseconds per elements of the Calibration /** @param nbEvent : number of events of the tables */ void evaluateCalibration(size_t nbEvent){ //Let's define size of data : size_t nbPixel(NB_PIXEL), nbSlice(NB_SLICE); size_t nbElement(nbEvent*nbSlice*nbPixel); VecData vecGain(nbPixel), vecPedestal(nbPixel); std::fill(vecGain.begin(), vecGain.end(), 0.02f); std::fill(vecPedestal.begin(), vecPedestal.end(), 40.0f); VecData vecADCSignal(nbElement), vecCalibSignal(nbElement); std::fill(vecADCSignal.begin(), vecADCSignal.end(), 42.0f); VecIndex vecIdx(nbElement); //Init vector of index for the computation std::iota(vecIdx.begin(), vecIdx.end(), 0); //Hope some day views will work to avoid allocation of index vector std::transform(std::execution::par_unseq, vecIdx.begin(), vecIdx.end(), vecIdx.begin(), [=](int i){ //Modulo computation, only once at the initialisation time return i % nbPixel; } ); float *tabGain = vecGain.data(), *tabPed = vecPedestal.data(); size_t fullNbElement(nbElement); micro_benchmarkAutoNsPrint("evaluateCalibration nvc++ pre computed modulo", fullNbElement, compute_calibration, vecCalibSignal, vecADCSignal, tabPed, tabGain, vecIdx); } int main(int argc, char** argv){ return micro_benchmarkParseArg(argc, argv, evaluateCalibration); } |