MaCh3  2.8.0
Reference Guide
gpuSplineUtils.cu
Go to the documentation of this file.
1 //MaCh3 included
3 
4 // KS: Forgive me father, for I have sinned.
5 #if defined(__CUDA_ARCH__)
6  #if __CUDA_ARCH__ >= 1200
7  #pragma message("Compiling with CUDA Architecture: 12.x")
8  #elif __CUDA_ARCH__ >= 1100
9  #pragma message("Compiling with CUDA Architecture: 11.x")
10  #elif __CUDA_ARCH__ >= 1000
11  #pragma message("Compiling with CUDA Architecture: 10.x")
12  #elif __CUDA_ARCH__ >= 900
13  #pragma message("Compiling with CUDA Architecture: 9.x")
14  #elif __CUDA_ARCH__ >= 800
15  #pragma message("Compiling with CUDA Architecture: 8.x")
16  #elif __CUDA_ARCH__ >= 750
17  #pragma message("Compiling with CUDA Architecture: 7.5")
18  #elif __CUDA_ARCH__ >= 730
19  #pragma message("Compiling with CUDA Architecture: 7.3")
20  #elif __CUDA_ARCH__ >= 720
21  #pragma message("Compiling with CUDA Architecture: 7.2")
22  #elif __CUDA_ARCH__ >= 710
23  #pragma message("Compiling with CUDA Architecture: 7.1")
24  #elif __CUDA_ARCH__ >= 700
25  #pragma message("Compiling with CUDA Architecture: 7.x")
26  #elif __CUDA_ARCH__ >= 650
27  #pragma message("Compiling with CUDA Architecture: 6.5")
28  #elif __CUDA_ARCH__ >= 600
29  #pragma message("Compiling with CUDA Architecture: 6.x")
30  #elif __CUDA_ARCH__ >= 530
31  #pragma message("Compiling with CUDA Architecture: 5.3")
32  #elif __CUDA_ARCH__ >= 520
33  #pragma message("Compiling with CUDA Architecture: 5.2")
34  #elif __CUDA_ARCH__ >= 510
35  #pragma message("Compiling with CUDA Architecture: 5.1")
36  #elif __CUDA_ARCH__ >= 500
37  #pragma message("Compiling with CUDA Architecture: 5.x")
38  #elif __CUDA_ARCH__ >= 400
39  #pragma message("Compiling with CUDA Architecture: 4.x")
40  #elif __CUDA_ARCH__ >= 300
41  #pragma message("Compiling with CUDA Architecture: 3.x")
42  #else
43  #pragma message("Compiling with CUDA Architecture: < 3.x")
44  #endif
45 #endif
46 
47 
48 // *****************************************
49 // Make sure all Cuda threads finished execution
50 __host__ void SynchroniseSplines() {
51  cudaDeviceSynchronize();
52 }
53 
54 // *******************************************
55 // INITIALISE GPU
56 // *******************************************
57 
60  cpu_n_params = -1;
61  cpu_n_events = -1;
62  cpu_n_TF1 = 0;
63  cpu_n_splines = 0;
64  cpu_spline_size = 0;
65 
66  gpu_weights = nullptr;
67  gpu_total_weights = nullptr;
68  gpu_nParamPerEvent = nullptr;
69  gpu_nPoints_arr = nullptr;
70  gpu_paramNo_arr = nullptr;
71  gpu_nKnots_arr = nullptr;
72  gpu_coeff_x = nullptr;
73  gpu_coeff_many = nullptr;
74  gpu_coeff_TF1_many = nullptr;
75  gpu_paramNo_TF1_arr = nullptr;
76  gpu_nParamPerEvent_TF1 = nullptr;
77  gpu_weights_tf1 = nullptr;
78 }
79 
80 // *******************************************
82 // *******************************************
83  cudaFree(gpu_paramNo_arr);
84  cudaFree(gpu_nKnots_arr);
85 
86  // free the coefficient arrays
87  cudaDestroyTextureObject(text_coeff_x);
88  cudaFree(gpu_coeff_x);
89  cudaFree(gpu_coeff_many);
90 
91  cudaFree(gpu_par_val);
92  cudaFree(gpu_spline_segment);
93 
94  cudaFree(gpu_coeff_TF1_many);
95  cudaFree(gpu_paramNo_TF1_arr);
96  // free weights on the gpu
97  cudaFree(gpu_weights);
98  cudaFree(gpu_weights_tf1);
99  cudaFree(gpu_total_weights);
100  //KS: Before removing variable let's destroy texture
101  cudaDestroyTextureObject(text_nParamPerEvent);
102  cudaDestroyTextureObject(text_nParamPerEvent_TF1);
103 
104  cudaFree(gpu_nParamPerEvent);
105  cudaFree(gpu_nParamPerEvent_TF1);
106 }
107 
108 // *******************************************
109 // Initialiser when using the x array and combined y,b,c,d array
111  unsigned int total_nknots,
112  unsigned int n_splines,
113  unsigned int n_tf1,
114  int Eve_size) {
115 // *******************************************
116  // Allocate chunks of memory to GPU
117  cudaMalloc((void **) &gpu_paramNo_arr, n_splines*sizeof(short int));
118  CudaCheckError();
119 
120  cudaMalloc((void **) &gpu_nKnots_arr, n_splines*sizeof(unsigned int));
121  CudaCheckError();
122 
123  cudaMalloc((void **) &gpu_coeff_x, Eve_size*sizeof(float));
124  CudaCheckError();
125 
126  cudaMalloc((void **) &gpu_coeff_many, _nCoeff_*total_nknots*sizeof(float));
127  CudaCheckError();
128 
129  // Allocate memory for the array of weights to be returned to CPU
130  cudaMalloc((void **) &gpu_weights, n_splines*sizeof(float));
131  CudaCheckError();
132 
133  // Now TF1 specific
134  cudaMalloc((void **) &gpu_coeff_TF1_many, _nTF1Coeff_*n_tf1*sizeof(float));
135  CudaCheckError();
136 
137  cudaMalloc((void **) &gpu_weights_tf1, n_tf1*sizeof(float));
138  CudaCheckError();
139 
140  cudaMalloc((void **) &gpu_paramNo_TF1_arr, n_tf1*sizeof(short int));
141  CudaCheckError();
142 
143  // Print allocation info to user
144  printf("Allocated %i entries for paramNo and nKnots arrays, size = %f MB\n",
145  n_splines, static_cast<double>(sizeof(short int) * n_splines + sizeof(unsigned int) * n_splines) / 1.0e6);
146  printf("Allocated %i entries for x coeff arrays, size = %f MB\n",
147  Eve_size, static_cast<double>(sizeof(float) * Eve_size) / 1.0e6);
148  printf("Allocated %i entries for {ybcd} coeff arrays, size = %f MB\n",
149  _nCoeff_ * total_nknots, static_cast<double>(sizeof(float) * _nCoeff_ * total_nknots) / 1.0e6);
150  printf("Allocated %i entries for TF1 coefficient arrays, size = %f MB\n",
151  _nTF1Coeff_ * n_tf1, static_cast<double>(sizeof(float) * _nTF1Coeff_ * n_tf1) / 1.0e6);
152 
153  //KS: Ask CUDA about memory usage
154  checkGpuMem();
155  PrintNdevices();
156 }
157 
158 // *******************************************
160  M3::float_t **cpu_total_weights,
161  int n_events) {
162 // *******************************************
163  //KS: Rather than allocate memory in standard way this fancy cuda tool allows to pin host memory which make memory transfer faster
164  cudaMallocHost((void **) cpu_total_weights, n_events*sizeof(M3::float_t));
165  CudaCheckError();
166 
167  //KS: Allocate memory for the array of total weights to be returned to CPU
168  cudaMalloc((void **) &gpu_total_weights, n_events*sizeof(M3::float_t));
169  CudaCheckError();
170 
171  //KS: Allocate memory for the map keeping track how many splines each parameter has
172  cudaMalloc((void **) &gpu_nParamPerEvent, 2*n_events*sizeof(unsigned int));
173  CudaCheckError();
174 
175  //KS: Allocate memory for the map keeping track how many TF1 each parameter has
176  cudaMalloc((void **) &gpu_nParamPerEvent_TF1, 2*n_events*sizeof(unsigned int));
177  CudaCheckError();
178 }
179 
180 // *******************************************
181 // Allocate memory for spline segments
182 __host__ void SplineMonolithGPU::InitGPU_Segments(short int **segment) {
183 // *******************************************
184  //KS: Rather than allocate memory in standard way this fancy cuda tool allows to pin host memory which make memory transfer faster
185  cudaMallocHost((void **) segment, cpu_n_params*sizeof(short int));
186  CudaCheckError();
187 }
188 
189 // *******************************************
190 // Allocate memory for spline segments
191 __host__ void SplineMonolithGPU::InitGPU_Vals(float **vals) {
192 // *******************************************
193  //KS: Rather than allocate memory in standard way this fancy cuda tool allows to pin host memory which make memory transfer faster
194  cudaMallocHost((void **) vals, cpu_n_params*sizeof(float));
195  CudaCheckError();
196 }
197 
198 // ******************************************************
199 // START COPY TO GPU
200 // ******************************************************
201 
202 // ******************************************************
203 // Copy to GPU for x array and separate ybcd array
205  const SplineMonoStruct* cpu_spline_handler,
206 
207  // TFI related now
208  const std::vector<float>& cpu_many_array_TF1,
209  const std::vector<short int>& cpu_paramNo_arr_TF1,
210  const int n_events,
211  const std::vector<unsigned int>& cpu_nParamPerEvent,
212  // TFI related now
213  const std::vector<unsigned int>& cpu_nParamPerEvent_TF1,
214 
215  const int n_params,
216  const unsigned int n_splines,
217  const short int spline_size,
218  const unsigned int total_nknots,
219  const unsigned int n_tf1) {
220 // ******************************************************
221  // Write to the global statics (h_* denotes host stored variable)
222  cpu_n_params = n_params;
223  // Number of events
224  cpu_n_events = n_events;
225  // Total number of valid TF1 for all loaded events
226  cpu_n_TF1 = n_tf1;
227  // Total number of valid splines for all loaded events
228  cpu_n_splines = n_splines;
230  cpu_spline_size = spline_size;
231 
232  //CW: Allocate memory for the frequently copied objects
233  cudaMalloc(&gpu_par_val, n_params * sizeof(float));
234  cudaMalloc(&gpu_spline_segment, n_params * sizeof(short int));
235 
236  // Copy the coefficient arrays to the GPU; this only happens once per entire Markov Chain so is OK to do multiple extensive memory copies
237  cudaMemcpy(gpu_coeff_many, cpu_spline_handler->coeff_many.data(), sizeof(float)*total_nknots*_nCoeff_, cudaMemcpyHostToDevice);
238  CudaCheckError();
239 
240  cudaMemcpy(gpu_coeff_x, cpu_spline_handler->coeff_x.data(), sizeof(float)*spline_size*n_params, cudaMemcpyHostToDevice);
241  CudaCheckError();
242 
243  //KS: Bind our texture with the GPU variable
244  //KS: Tried also moving gpu_many_array to texture memory it only worked with restricted number of MC runs, most likely hit texture memory limit :(
245  struct cudaResourceDesc resDesc_coeff_x;
246  memset(&resDesc_coeff_x, 0, sizeof(resDesc_coeff_x));
247  resDesc_coeff_x.resType = cudaResourceTypeLinear;
248  resDesc_coeff_x.res.linear.devPtr = gpu_coeff_x;
249  resDesc_coeff_x.res.linear.desc = cudaCreateChannelDesc<float>();
250  resDesc_coeff_x.res.linear.sizeInBytes = sizeof(float)*spline_size*n_params;
251 
252  // Specify texture object parameters
253  struct cudaTextureDesc texDesc_coeff_x;
254  memset(&texDesc_coeff_x, 0, sizeof(texDesc_coeff_x));
255  texDesc_coeff_x.readMode = cudaReadModeElementType;
256 
257  // Create texture object
258  cudaCreateTextureObject(&text_coeff_x, &resDesc_coeff_x, &texDesc_coeff_x, nullptr);
259  CudaCheckError();
260 
261  // Also copy the parameter number for each spline onto the GPU; i.e. what spline parameter are we calculating right now
262  cudaMemcpy(gpu_paramNo_arr, cpu_spline_handler->paramNo_arr.data(), n_splines*sizeof(short int), cudaMemcpyHostToDevice);
263  CudaCheckError();
264 
265  // Also copy the knot map for each spline onto the GPU;
266  cudaMemcpy(gpu_nKnots_arr, cpu_spline_handler->nKnots_arr.data(), n_splines*sizeof(unsigned int), cudaMemcpyHostToDevice);
267  CudaCheckError();
268 
269  //Now TF1
270  // Copy the coefficient arrays to the GPU; this only happens once per entire Markov Chain so is OK to do multiple extensive memory copies
271  cudaMemcpy(gpu_coeff_TF1_many, cpu_many_array_TF1.data(), sizeof(float)*n_tf1*_nTF1Coeff_, cudaMemcpyHostToDevice);
272  CudaCheckError();
273 
274  // Also copy the parameter number for each TF1 onto the GPU; i.e. what TF1 parameter are we calculating right now
275  cudaMemcpy(gpu_paramNo_TF1_arr, cpu_paramNo_arr_TF1.data(), n_tf1*sizeof(short int), cudaMemcpyHostToDevice);
276  CudaCheckError();
277 
278  //KS: Keep track how much splines each event has
279  cudaMemcpy(gpu_nParamPerEvent, cpu_nParamPerEvent.data(), 2*n_events*sizeof(unsigned int), cudaMemcpyHostToDevice);
280  CudaCheckError();
281 
282  //KS: Bind our texture with the GPU variable
283  // create a resource descriptor based on device pointers
284  struct cudaResourceDesc resDesc_nParamPerEvent;
285  memset(&resDesc_nParamPerEvent, 0, sizeof(resDesc_nParamPerEvent));
286  resDesc_nParamPerEvent.resType = cudaResourceTypeLinear;
287  resDesc_nParamPerEvent.res.linear.devPtr = gpu_nParamPerEvent;
288  resDesc_nParamPerEvent.res.linear.desc = cudaCreateChannelDesc<unsigned int>();
289  resDesc_nParamPerEvent.res.linear.sizeInBytes = 2*n_events*sizeof(unsigned int);
290 
291  // Specify texture object parameters
292  struct cudaTextureDesc texDesc_nParamPerEvent;
293  memset(&texDesc_nParamPerEvent, 0, sizeof(texDesc_nParamPerEvent));
294  texDesc_nParamPerEvent.readMode = cudaReadModeElementType;
295 
296  //Finally create texture object
297  cudaCreateTextureObject(&text_nParamPerEvent, &resDesc_nParamPerEvent, &texDesc_nParamPerEvent, nullptr);
298  CudaCheckError();
299 
300  // Now TF1
301  cudaMemcpy(gpu_nParamPerEvent_TF1, cpu_nParamPerEvent_TF1.data(), 2*n_events*sizeof(unsigned int), cudaMemcpyHostToDevice);
302  CudaCheckError();
303 
304  //KS: Bind our texture with the GPU variable
305  // create a resource descriptor based on device pointers
306  struct cudaResourceDesc resDesc_nParamPerEvent_tf1;
307  memset(&resDesc_nParamPerEvent_tf1, 0, sizeof(resDesc_nParamPerEvent_tf1));
308  resDesc_nParamPerEvent_tf1.resType = cudaResourceTypeLinear;
309  resDesc_nParamPerEvent_tf1.res.linear.devPtr = gpu_nParamPerEvent_TF1;
310  resDesc_nParamPerEvent_tf1.res.linear.desc = cudaCreateChannelDesc<unsigned int>();
311  resDesc_nParamPerEvent_tf1.res.linear.sizeInBytes = 2*n_events*sizeof(unsigned int);
312 
313  // Specify texture object parameters
314  struct cudaTextureDesc texDesc_nParamPerEvent_tf1;
315  memset(&texDesc_nParamPerEvent_tf1, 0, sizeof(texDesc_nParamPerEvent_tf1));
316  texDesc_nParamPerEvent_tf1.readMode = cudaReadModeElementType;
317 
318  //Finally create texture object
319  cudaCreateTextureObject(&text_nParamPerEvent_TF1, &resDesc_nParamPerEvent_tf1, &texDesc_nParamPerEvent_tf1, nullptr);
320  CudaCheckError();
321 }
322 
323 // ******************************************************
324 // Copy to GPU for x array and separate ybcd array
326  M3::float_t *manycoeff_arr,
327  M3::float_t *xcoeff_arr,
328  const std::vector<short int>& uniquesplinevec_Monolith,
329  const std::vector<unsigned int>& coeffindexvec,
330 
331  const int n_params,
332  const unsigned int n_splines,
333  const short int spline_size,
334  const unsigned int total_nknots) {
335 // ******************************************************
336  // Write to the global statics (h_* denotes host stored variable)
337  cpu_n_params = n_params;
338  // Total number of valid splines for all loaded events
339  cpu_n_splines = n_splines;
341  cpu_spline_size = spline_size;
342 
343 
344  //CW: Allocate memory for the frequently copied objects
345  cudaMalloc(&gpu_par_val, n_params * sizeof(float));
346  cudaMalloc(&gpu_spline_segment, n_params * sizeof(short int));
347  #ifndef _LOW_MEMORY_STRUCTS_
349  // Convert M3::float_t -> float for GPU
350  std::vector<float> coeff_many_float(manycoeff_arr, manycoeff_arr + total_nknots * _nCoeff_);
351  std::vector<float> coeff_x_float(xcoeff_arr, xcoeff_arr + spline_size * n_params);
352 
353  // Copy the coefficient arrays to the GPU; this only happens once per entire Markov Chain so is OK to do multiple extensive memory copies
354  cudaMemcpy(gpu_coeff_many, coeff_many_float.data(), sizeof(float)*total_nknots*_nCoeff_, cudaMemcpyHostToDevice);
355  CudaCheckError();
356 
357  cudaMemcpy(gpu_coeff_x, coeff_x_float.data(), sizeof(float)*spline_size*n_params, cudaMemcpyHostToDevice);
358  CudaCheckError();
359  #else
360  // Copy the coefficient arrays to the GPU; this only happens once per entire Markov Chain so is OK to do multiple extensive memory copies
361  cudaMemcpy(gpu_coeff_many, manycoeff_arr, sizeof(float)*total_nknots*_nCoeff_, cudaMemcpyHostToDevice);
362  CudaCheckError();
363 
364  cudaMemcpy(gpu_coeff_x, xcoeff_arr, sizeof(float)*spline_size*n_params, cudaMemcpyHostToDevice);
365  CudaCheckError();
366  #endif
367  //KS: Bind our texture with the GPU variable
368  //KS: Tried also moving gpu_many_array to texture memory it only worked with restricted number of MC runs, most likely hit texture memory limit :(
369  struct cudaResourceDesc resDesc_coeff_x;
370  memset(&resDesc_coeff_x, 0, sizeof(resDesc_coeff_x));
371  resDesc_coeff_x.resType = cudaResourceTypeLinear;
372  resDesc_coeff_x.res.linear.devPtr = gpu_coeff_x;
373  resDesc_coeff_x.res.linear.desc = cudaCreateChannelDesc<float>();
374  resDesc_coeff_x.res.linear.sizeInBytes = sizeof(float)*spline_size*n_params;
375 
376  // Specify texture object parameters
377  struct cudaTextureDesc texDesc_coeff_x;
378  memset(&texDesc_coeff_x, 0, sizeof(texDesc_coeff_x));
379  texDesc_coeff_x.readMode = cudaReadModeElementType;
380 
381  // Create texture object
382  cudaCreateTextureObject(&text_coeff_x, &resDesc_coeff_x, &texDesc_coeff_x, nullptr);
383  CudaCheckError();
384 
385  // Also copy the parameter number for each spline onto the GPU; i.e. what spline parameter are we calculating right now
386  cudaMemcpy(gpu_paramNo_arr, uniquesplinevec_Monolith.data(), n_splines*sizeof(short int), cudaMemcpyHostToDevice);
387  CudaCheckError();
388 
389  // Also copy the knot map for each spline onto the GPU;
390  cudaMemcpy(gpu_nKnots_arr, coeffindexvec.data(), n_splines*sizeof(unsigned int), cudaMemcpyHostToDevice);
391  CudaCheckError();
392 }
393 
394 // ********************************************************
395 // START GPU KERNELS
396 //*********************************************************
397 // All the GPU kernels have similar tasks but different implementations
398 // Essentially they perform a binary search to find which TSpline3 point is nearest to our parameter variation
399 // Once it knows this, we simply extract the pre-computed coefficients for that spline point and multiply together to get a weight
400 
401 //*********************************************************
402 // Evaluate the spline on the GPU Using one {y,b,c,d} array and one {x} array
403 // Should be most efficient at cache hitting and memory coalescence
404 // But using spline segments rather than the parameter value: avoids doing binary search on GPU
405 __global__ void EvalOnGPU_Splines(
406  const unsigned int gpu_n_splines,
407  const short int gpu_spline_size,
408  const short int* __restrict__ gpu_paramNo_arr,
409  const unsigned int* __restrict__ gpu_nKnots_arr,
410  const float* __restrict__ gpu_coeff_many,
411  const float* __restrict__ gpu_par_val,
412  const short int* __restrict__ gpu_spline_segment,
413  float* __restrict__ gpu_weights,
414  const cudaTextureObject_t __restrict__ text_coeff_x) {
415 //*********************************************************
416  // points per spline is the offset to skip in the index to move between splines
417  const unsigned int splineNum = (blockIdx.x * blockDim.x + threadIdx.x);
418 
419  // this is the stopping condition!
420  if (splineNum < gpu_n_splines) {
421  // This is the segment we want for this parameter variation
422  // for this particular splineNum; 0 = MACCQE, 1 = pFC, 2 = EBC, etc
423 
424  //CW: Which Parameter we are accessing
425  const short int Param = gpu_paramNo_arr[splineNum];
426 
427  //CW: Avoids doing costly binary search on GPU
428  const short int segment = gpu_spline_segment[Param];
429 
430  //KS: Segment for coeff_x is simply parameter*max knots + segment as each parmeters has the same spacing
431  const short int segment_X = Param*gpu_spline_size+segment;
432 
433  //KS: Find knot position in out monolitical structure
434  const unsigned int CurrentKnotPos = gpu_nKnots_arr[splineNum]*_nCoeff_+segment*_nCoeff_;
435 
436  // We've read the segment straight from CPU and is saved in gpu_spline_segment
437  // polynomial parameters from the monolithic splineMonolith
438  const float fY = gpu_coeff_many[CurrentKnotPos];
439  const float fB = gpu_coeff_many[CurrentKnotPos + 1];
440  const float fC = gpu_coeff_many[CurrentKnotPos + 2];
441  const float fD = gpu_coeff_many[CurrentKnotPos + 3];
442  // The is the variation itself (needed to evaluate variation - stored spline point = dx)
443  const float dx = gpu_par_val[Param] - tex1Dfetch<float>(text_coeff_x, segment_X);
444 
445  //CW: Wooow, let's use some fancy intrinsics and pull down the processing time by <1% from normal multiplication! HURRAY
446  gpu_weights[splineNum] = fmaf(dx, fmaf(dx, fmaf(dx, fD, fC), fB), fY);
447  // Or for the more "easy to read" version:
448  //gpu_weights[splineNum] = (fY+dx*(fB+dx*(fC+dx*fD)));
449 
450  //#ifdef MACH3_DEBUG
451  //printf("splineNum = %i/%i, paramNo = %i, variation = %f, segment = %i, fX = %f, fX+1 = %f, dx = %f, gpu_n_splines = %i, gpu_spline_size = %i, weight = %f \n", splineNum, gpu_n_splines, gpu_paramNo_arr[splineNum], gpu_par_val[Param], segment, tex1Dfetch<float>(text_coeff_x, segment_X), tex1Dfetch<float>(text_coeff_x, segment_X+1), dx, gpu_n_splines, gpu_spline_size, gpu_weights[splineNum]);
452  //#endif
453  }
454 }
455 
456 //*********************************************************
457 // Evaluate the TF1 on the GPU Using 5th order polynomial
458 __global__ void EvalOnGPU_TF1(
459  const unsigned int gpu_n_TF1,
460  const float* __restrict__ gpu_coeffs_tf1,
461  const short int* __restrict__ gpu_paramNo_arr_tf1,
462  const float* __restrict__ gpu_par_val,
463  float* __restrict__ gpu_weights_tf1) {
464 //*********************************************************
465  // points per spline is the offset to skip in the index to move between splines
466  const unsigned int tf1Num = (blockIdx.x * blockDim.x + threadIdx.x);
467 
468  if (tf1Num < gpu_n_TF1) {
469  // The is the variation itself (needed to evaluate variation - stored spline point = dx)
470  const float x = gpu_par_val[gpu_paramNo_arr_tf1[tf1Num]];
471 
472  // Read the coefficients
473  const unsigned int TF1_Index = tf1Num * _nTF1Coeff_;
474  const float a = gpu_coeffs_tf1[TF1_Index];
475  const float b = gpu_coeffs_tf1[TF1_Index+1];
476 
477  gpu_weights_tf1[tf1Num] = fmaf(a, x, b);
478  // gpu_weights_tf1[tf1Num] = a*x + b;
479  // gpu_weights_tf1[tf1Num] = 1 + a*x + b*x*x + c*x*x*x + d*x*x*x*x + e*x*x*x*x*x;
480  }
481 }
482 
483 //*********************************************************
484 // KS: Evaluate the total spline event weight on the GPU, as in most cases GPU is faster, even more this significant reduce memory transfer from GPU to CPU
485 __global__ void EvalOnGPU_TotWeight(
486  const int gpu_n_events,
487  const float* __restrict__ gpu_weights,
488  const float* __restrict__ gpu_weights_tf1,
489 
490  M3::float_t* __restrict__ gpu_total_weights,
491 
492  const cudaTextureObject_t __restrict__ text_nParamPerEvent,
493  const cudaTextureObject_t __restrict__ text_nParamPerEvent_TF1) {
494 //*********************************************************
495  const unsigned int EventNum = (blockIdx.x * blockDim.x + threadIdx.x);
496 
497  if(EventNum < gpu_n_events) //stopping condition
498  {
499  float local_total_weight = 1.f;
500 
501  const unsigned int EventOffset = 2 * EventNum;
502 
503  for (unsigned int id = 0; id < tex1Dfetch<unsigned int>(text_nParamPerEvent, EventOffset); ++id) {
504  local_total_weight *= gpu_weights[tex1Dfetch<unsigned int>(text_nParamPerEvent, EventOffset+1) + id];
505  }
506 
507  for (unsigned int id = 0; id < tex1Dfetch<unsigned int>(text_nParamPerEvent_TF1, EventOffset); ++id) {
508  local_total_weight *= gpu_weights_tf1[tex1Dfetch<unsigned int>(text_nParamPerEvent_TF1, EventOffset+1) + id];
509  }
510  gpu_total_weights[EventNum] = static_cast<M3::float_t>(local_total_weight);
511  }
512 }
513 
514 // *****************************************
515 // Run the GPU code for the separate many arrays. As in separate {x}, {y,b,c,d} arrays
516 // Pass the segment and the parameter values
517 // (binary search already performed in SplineBase::FindSplineSegment()
519  M3::float_t* cpu_total_weights,
520  // Holds the changes in parameters
521  float *vals,
522  // Holds the segments for parameters
523  short int *segment) {
524 // *****************************************
525  dim3 block_size;
526  dim3 grid_size;
527 
528  block_size.x = _BlockSize_;
529  grid_size.x = (cpu_n_splines / block_size.x) + 1;
530 
531  // Copy the segment values to the GPU (segment_gpu), which is cpu_n_params long
532  cudaMemcpy(gpu_spline_segment, segment, cpu_n_params * sizeof(short int), cudaMemcpyHostToDevice);
533  CudaCheckError();
534 
535  // Copy the parameter values values to the GPU (vals_gpu), which is cpu_n_params long
536  cudaMemcpy(gpu_par_val, vals, cpu_n_params * sizeof(float), cudaMemcpyHostToDevice);
537  CudaCheckError();
538 
539  // KS: Consider asynchronous kernel call, this might help EvalOnGPU_Splines and EvalOnGPU_TF1 are independent
540  // Set the cache config to prefer L1 for the kernel
541  //cudaFuncSetCacheConfig(EvalOnGPU_Splines, cudaFuncCachePreferL1);
542  EvalOnGPU_Splines<<<grid_size, block_size>>>(
548  gpu_par_val,
550 
551  gpu_weights,
553  );
554  CudaCheckError();
555 
556  grid_size.x = (cpu_n_TF1 / block_size.x) + 1;
557  EvalOnGPU_TF1<<<grid_size, block_size>>>(
558  cpu_n_TF1,
561  gpu_par_val,
563  );
564  CudaCheckError();
565 
566  grid_size.x = (cpu_n_events / block_size.x) + 1;
567 
568  EvalOnGPU_TotWeight<<<grid_size, block_size>>>(
569  cpu_n_events,
570  gpu_weights,
572 
574 
577  );
578  CudaCheckError();
579 
580  //KS: Here we have to make a somewhat large GPU->CPU transfer because it is proportional to number of events
581  //KS: Normally code wait for memory transfer to finish before moving further cudaMemcpyAsync means we will continue to execute code and in a meantime keep copying stuff.
582  cudaMemcpyAsync(cpu_total_weights, gpu_total_weights, cpu_n_events * sizeof(M3::float_t), cudaMemcpyDeviceToHost, 0);
583  CudaCheckError();
584 
585  #ifdef MACH3_DEBUG
586  printf("Copied GPU total weights to CPU with SUCCESS (drink more tea)\n");
587  printf("Released calculated response from GPU with SUCCESS (drink most tea)\n");
588  #endif
589 }
590 
591 // *****************************************
592 // Run the GPU code for the separate many arrays. As in separate {x}, {y,b,c,d} arrays
593 // Pass the segment and the parameter values
594 // (binary search already performed in SplineBase::FindSplineSegment()
596  M3::float_t* cpu_spline_weights,
597  // Holds the changes in parameters
598  float *vals,
599  // Holds the segments for parameters
600  short int *segment) {
601 // *****************************************
602  dim3 block_size;
603  dim3 grid_size;
604 
605  block_size.x = _BlockSize_;
606  grid_size.x = (cpu_n_splines / block_size.x) + 1;
607 
608  // Copy the segment values to the GPU (segment_gpu), which is cpu_n_params long
609  cudaMemcpy(gpu_spline_segment, segment, cpu_n_params * sizeof(short int), cudaMemcpyHostToDevice);
610  CudaCheckError();
611 
612  // Copy the parameter values values to the GPU (vals_gpu), which is cpu_n_params long
613  cudaMemcpy(gpu_par_val, vals, cpu_n_params * sizeof(float), cudaMemcpyHostToDevice);
614  CudaCheckError();
615 
616  // KS: Consider asynchronous kernel call, this might help EvalOnGPU_Splines and EvalOnGPU_TF1 are independent
617  // Set the cache config to prefer L1 for the kernel
618  //cudaFuncSetCacheConfig(EvalOnGPU_Splines, cudaFuncCachePreferL1);
619  EvalOnGPU_Splines<<<grid_size, block_size>>>(
625  gpu_par_val,
627 
628  gpu_weights,
630  );
631  CudaCheckError();
632 
633  #ifndef _LOW_MEMORY_STRUCTS_
634  // Here we have to make a somewhat large GPU->CPU transfer because it's all the splines' response
635  cudaMemcpy(cpu_tmp_weights.data(), gpu_weights, cpu_n_splines * sizeof(float), cudaMemcpyDeviceToHost);
636  CudaCheckError();
637  // KS: we need to perform conversion from float (this is what GPU uses) to M3::float_t
639  for (unsigned int i = 0; i < cpu_n_splines; ++i) {
640  cpu_spline_weights[i] = static_cast<M3::float_t>(cpu_tmp_weights[i]);
641  }
642  #else
643  // Here we have to make a somewhat large GPU->CPU transfer because it's all the splines' response
645  cudaMemcpy(cpu_spline_weights, gpu_weights, cpu_n_splines * sizeof(float), cudaMemcpyDeviceToHost);
646  CudaCheckError();
647  #endif
648 
649  #ifdef MACH3_DEBUG
650  printf("Copied GPU total weights to CPU with SUCCESS (drink more tea)\n");
651  printf("Released calculated response from GPU with SUCCESS (drink most tea)\n");
652  #endif
653 }
654 
655 // *******************************************
657 __host__ void SplineMonolithGPU::CleanupPinnedMemory(M3::float_t *cpu_total_weights,
658  short int *segment, float *vals) {
659 // *******************************************
660  if(cpu_total_weights != nullptr) cudaFreeHost(cpu_total_weights);
661  cudaFreeHost(segment);
662  cudaFreeHost(vals);
663 
664  cpu_total_weights = nullptr;
665  segment = nullptr;
666  vals = nullptr;
667 }
constexpr int _nCoeff_
KS: We store coefficients {y,b,c,d} in one array one by one, this is only to define it once rather th...
Definition: SplineCommon.h:18
constexpr int _nTF1Coeff_
KS: For TF1 we store at most 5 coefficients, we could make it more flexible but for now define it her...
Definition: SplineCommon.h:20
float * gpu_weights
GPU arrays to hold weight for each spline.
unsigned int * gpu_nParamPerEvent
KS: GPU map keeping track how many parameters applies to each event, we keep two numbers here {number...
__host__ void InitGPU_SplineMonolith(unsigned int total_nknots, unsigned int n_splines, unsigned int n_tf1, int Eve_size)
Allocate memory on gpu for spline monolith.
unsigned int cpu_n_TF1
Number of tf1 living on CPU.
virtual ~SplineMonolithGPU()
This function deallocates the resources allocated for the separate {x} and {ybcd} arrays in the and T...
unsigned int * gpu_nParamPerEvent_TF1
KS: GPU map keeping track how many parameters applies to each event, we keep two numbers here {number...
float * gpu_weights_tf1
GPU arrays to hold weight for each TF1.
unsigned int * gpu_nKnots_arr
KS: GPU Number of knots per spline.
float * gpu_par_val
CW: parameter value on GPU.
float * gpu_coeff_TF1_many
GPU arrays to hold TF1 coefficients.
__host__ void InitGPU_Vals(float **vals)
Allocate memory for spline segments.
cudaTextureObject_t text_nParamPerEvent_TF1
KS: Map keeping track how many parameters applies to each event, we keep two numbers here {number of ...
__host__ void RunGPU_SplineMonolith_Unbinned(M3::float_t *cpu_total_weights, float *vals, short int *segment)
Run the GPU code for the separate many arrays. As in separate {x}, {y,b,c,d} arrays Pass the segment ...
float * gpu_coeff_many
GPU arrays to hold other coefficients.
__host__ void CleanupPinnedMemory(M3::float_t *cpu_total_weights, short int *segment, float *vals)
Clean up pinned variables at CPU.
__host__ void CopyToGPU_SplineMonolith_Unbinned(const SplineMonoStruct *cpu_spline_handler, const std::vector< float > &cpu_many_array_TF1, const std::vector< short int > &cpu_paramNo_arr_TF1, const int n_events, const std::vector< unsigned int > &cpu_nParamPerEvent, const std::vector< unsigned int > &cpu_nParamPerEvent_TF1, const int n_params, const unsigned int n_splines, const short int spline_size, const unsigned int total_nknots, const unsigned int n_tf1)
Copies data from CPU to GPU for the spline monolith.
short int * gpu_paramNo_TF1_arr
CW: GPU array with the number of points per TF1 object.
cudaTextureObject_t text_coeff_x
KS: Textures are L1 cache variables which are well optimised for fetching. Make texture only for vari...
int cpu_n_params
Number of params living on CPU.
__host__ void InitGPU_Segments(short int **segment)
Allocate memory for spline segments.
__host__ void InitGPU_Unbinned_SplineMonolith(M3::float_t **cpu_total_weights, int n_events)
Allocate memory on gpu for spline monolith.
int cpu_n_events
Number of events living on CPU.
short int * gpu_nPoints_arr
GPU arrays to hold number of points.
unsigned int cpu_n_splines
Number of splines living on CPU.
SplineMonolithGPU()
constructor
short int * gpu_paramNo_arr
CW: GPU array with the number of points per spline (not per spline point!)
float * gpu_coeff_x
KS: GPU arrays to hold X coefficient.
__host__ void RunGPU_SplineMonolith_Binned(M3::float_t *cpu_spline_weights, float *vals, short int *segment)
cudaTextureObject_t text_nParamPerEvent
KS: Map keeping track how many parameters applies to each event, we keep two numbers here {number of ...
std::vector< float > cpu_tmp_weights
Temporary weight needed for conversion from float to double.
short int cpu_spline_size
Size of splines living on CPU.
__host__ void CopyToGPU_SplineMonolith_Binned(M3::float_t *manycoeff_arr, M3::float_t *xcoeff_arr, const std::vector< short int > &uniquesplinevec_Monolith, const std::vector< unsigned int > &coeffindexvec, const int n_params, const unsigned int n_splines, const short int spline_size, const unsigned int total_nknots)
Copies data from CPU to GPU for the spline monolith.
M3::float_t * gpu_total_weights
GPU arrays to hold weight for event.
short int * gpu_spline_segment
CW: Spline segment on GPU.
__global__ void EvalOnGPU_TF1(const unsigned int gpu_n_TF1, const float *__restrict__ gpu_coeffs_tf1, const short int *__restrict__ gpu_paramNo_arr_tf1, const float *__restrict__ gpu_par_val, float *__restrict__ gpu_weights_tf1)
Evaluate the TF1 on the GPU Using first order polynomial Polynomial form: w(x) = a0 + a1·x.
__host__ void SynchroniseSplines()
Make sure all Cuda threads finished execution.
__global__ void EvalOnGPU_TotWeight(const int gpu_n_events, const float *__restrict__ gpu_weights, const float *__restrict__ gpu_weights_tf1, M3::float_t *__restrict__ gpu_total_weights, const cudaTextureObject_t __restrict__ text_nParamPerEvent, const cudaTextureObject_t __restrict__ text_nParamPerEvent_TF1)
KS: Evaluate the total spline event weight on the GPU, as in most cases GPU is faster,...
__global__ void EvalOnGPU_Splines(const unsigned int gpu_n_splines, const short int gpu_spline_size, const short int *__restrict__ gpu_paramNo_arr, const unsigned int *__restrict__ gpu_nKnots_arr, const float *__restrict__ gpu_coeff_many, const float *__restrict__ gpu_par_val, const short int *__restrict__ gpu_spline_segment, float *__restrict__ gpu_weights, const cudaTextureObject_t __restrict__ text_coeff_x)
Evaluate the spline on the GPU Using one {y,b,c,d} array and one {x} array Should be most efficient a...
MaCh3 event-by-event cross-section spline code.
void checkGpuMem()
KS: Get some fancy info about VRAM usage.
Definition: gpuUtils.cu:43
void PrintNdevices()
KS: Get some fancy info about GPU.
Definition: gpuUtils.cu:58
#define CudaCheckError()
Definition: gpuUtils.cuh:21
#define _BlockSize_
KS: Need it for shared memory, there is way to use dynamic shared memory but I am lazy right now.
Definition: gpuUtils.cuh:24
double float_t
Definition: Core.h:37
KS: Struct storing information for spline monolith.
Definition: SplineCommon.h:61
std::vector< unsigned int > nKnots_arr
KS: CPU Number of knots per spline.
Definition: SplineCommon.h:73
std::vector< float > coeff_x
KS: CPU arrays to hold X coefficient.
Definition: SplineCommon.h:67
std::vector< float > coeff_many
CPU arrays to hold other coefficients.
Definition: SplineCommon.h:70
std::vector< short int > paramNo_arr
CW: CPU array with the number of points per spline (not per spline point!)
Definition: SplineCommon.h:76