7.1.1.4.2 : Le main.cpp


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 définit notre kernel de calcul :
1
2
3
4
5
6
7
8
9
10
11
///Compute the calibration
/**	@param[out] vecCalibSignal : vector of calibrated signal
 * 	@param tabADC : pointer to the table 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
 * 	@param nbPixel : number of pixels in the camera
*/
void compute_calibration(std::vector<float> & vecCalibSignal,
			const float * tabADC, const float * tabPed, const float* tabGain, const std::vector<int> & vecIdx, int nbPixel)
{


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
6
	std::transform(std::execution::par_unseq, std::begin(vecIdx), std::end(vecIdx), std::begin(vecCalibSignal),
		       [=](int i){
				unsigned int idxPedGain = i % nbPixel;	//Prevent read of vecGain and vecPedestal outside of the bound
				return (tabADC[i] - tabPed[idxPedGain])*tabGain[idxPedGain];
	});
}


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
	std::vector<float> 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 :
1
2
	std::vector<float> vecADCSignal(nbElement), vecCalibSignal(nbElement);
	std::fill(vecADCSignal.begin(), vecADCSignal.end(), 42.0f);


On alloue et on initialise le tableau d'indices :
1
2
3
	std::vector<int> 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
	


On créé les pointeurs que l(on utilisera pour notre calcul :
1
2
	//We have to create pointer to be able to catch them by copy without losing any time
	float * tabADC = vecADCSignal.data(), *tabGain = vecGain.data(), *tabPed = vecPedestal.data();


On évalue la performance du kernel :
1
2
3
	size_t fullNbElement(nbElement);
	micro_benchmarkAutoNsPrint("evaluateCalibration nvc++", fullNbElement, compute_calibration, vecCalibSignal, tabADC, tabPed, tabGain, vecIdx, nbPixel);
}


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.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
/***************************************
	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"

///Compute the calibration
/**	@param[out] vecCalibSignal : vector of calibrated signal
 * 	@param tabADC : pointer to the table 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
 * 	@param nbPixel : number of pixels in the camera
*/
void compute_calibration(std::vector<float> & vecCalibSignal,
			const float * tabADC, const float * tabPed, const float* tabGain, const std::vector<int> & vecIdx, int nbPixel)
{
	std::transform(std::execution::par_unseq, std::begin(vecIdx), std::end(vecIdx), std::begin(vecCalibSignal),
		       [=](int i){
				unsigned int idxPedGain = i % nbPixel;	//Prevent read of vecGain and vecPedestal outside of the bound
				return (tabADC[i] - tabPed[idxPedGain])*tabGain[idxPedGain];
	});
}

///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);

	std::vector<float> vecGain(nbPixel), vecPedestal(nbPixel);
	std::fill(vecGain.begin(), vecGain.end(), 0.02f);
	std::fill(vecPedestal.begin(), vecPedestal.end(), 40.0f);

	std::vector<float> vecADCSignal(nbElement), vecCalibSignal(nbElement);
	std::fill(vecADCSignal.begin(), vecADCSignal.end(), 42.0f);

	std::vector<int> 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
	
	//We have to create pointer to be able to catch them by copy without losing any time
	float * tabADC = vecADCSignal.data(), *tabGain = vecGain.data(), *tabPed = vecPedestal.data();

	size_t fullNbElement(nbElement);
	micro_benchmarkAutoNsPrint("evaluateCalibration nvc++", fullNbElement, compute_calibration, vecCalibSignal, tabADC, tabPed, tabGain, vecIdx, nbPixel);
}

int main(int argc, char** argv){
	return micro_benchmarkParseArg(argc, argv, evaluateCalibration);
}
Le fichier main.cpp est disponible ici.