Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /include/VecGeom/backend/cuda/Interface.h was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /// \file cuda/Backend.h
0002 /// \author Johannes de Fine Licht (johannes.definelicht@cern.ch)
0003 
0004 #ifndef VECGEOM_BACKEND_CUDA_INTERFACE_H_
0005 #define VECGEOM_BACKEND_CUDA_INTERFACE_H_
0006 
0007 #include "VecGeom/base/Config.h"
0008 #include "VecGeom/base/Global.h"
0009 #include <driver_types.h>
0010 
0011 #ifdef VECGEOM_ENABLE_CUDA
0012 
0013 // Include CUDA API functions for host compilers to use the cudaError_t types
0014 // since the host compiler (.cc) includes this file transitively
0015 #include <cuda_runtime_api.h>
0016 
0017 #include <vector>
0018 #include <unordered_map>
0019 #include <type_traits>
0020 
0021 namespace vecgeom {
0022 
0023 #ifdef VECCORE_CUDA
0024 
0025 inline namespace cuda {
0026 
0027 template <typename DataClass, typename... ArgsTypes>
0028 __global__ void ConstructOnGpu(DataClass *gpu_ptr, ArgsTypes... params)
0029 {
0030   new (gpu_ptr) DataClass(params...);
0031 }
0032 
0033 template <typename DataClass, typename... ArgsTypes>
0034 __global__ void ConstructArrayOnGpu(DataClass *gpu_ptr, size_t nElements, ArgsTypes... params)
0035 {
0036 
0037   unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x;
0038 
0039   unsigned int idx = tid;
0040   while (idx < nElements) {
0041     new (gpu_ptr + idx) DataClass(params...);
0042     idx += blockDim.x * gridDim.x;
0043   }
0044 }
0045 
0046 /*!
0047  * Construct many objects on the GPU, whose addresses and parameters are passed as arrays.
0048  * \tparam DataClass Type of the objects to construct.
0049  * \param nElements Number of elements to construct. It is assumed that all argument arrays have this size.
0050  * \param gpu_ptrs  Array of pointers to place the new objects at.
0051  * \param params    Array(s) of constructor parameters for each object.
0052  */
0053 template <typename DataClass, typename... ArgsTypes>
0054 __global__ void ConstructManyOnGpu_kernel(size_t nElements, DataClass **gpu_ptrs, const ArgsTypes *...params)
0055 {
0056   const size_t tid = threadIdx.x + blockIdx.x * blockDim.x;
0057 
0058   for (size_t idx = tid; idx < nElements; idx += blockDim.x * gridDim.x) {
0059     new (gpu_ptrs[idx]) DataClass(params[idx]...);
0060   }
0061 }
0062 
0063 template <typename DataClass>
0064 __global__ void CopyBBoxesToGpu(size_t nElements, DataClass **raw_ptrs, Precision *boxes)
0065 {
0066   const size_t tid = threadIdx.x + blockIdx.x * blockDim.x;
0067 
0068   for (size_t idx = tid; idx < nElements; idx += blockDim.x * gridDim.x) {
0069     raw_ptrs[idx]->SetBBox({boxes[6 * idx], boxes[6 * idx + 1], boxes[6 * idx + 2]},
0070                            {boxes[6 * idx + 3], boxes[6 * idx + 4], boxes[6 * idx + 5]});
0071   }
0072 }
0073 
0074 } // namespace cuda
0075 
0076 #endif
0077 
0078 #ifdef VECCORE_CUDA
0079 namespace cxx {
0080 #else
0081 inline namespace cxx {
0082 #endif
0083 
0084 template <typename Type>
0085 Type *AllocateOnGpu(const unsigned int size)
0086 {
0087   Type *ptr = nullptr;
0088   VECGEOM_DEVICE_API_CALL(Malloc((void **)&ptr, size));
0089   return ptr;
0090 }
0091 
0092 template <typename Type>
0093 Type *AllocateOnGpu()
0094 {
0095   return AllocateOnGpu<Type>(sizeof(Type));
0096 }
0097 
0098 template <typename Type>
0099 void FreeFromGpu(Type *const ptr)
0100 {
0101   VECGEOM_DEVICE_API_CALL(Free(ptr));
0102 }
0103 
0104 template <typename Type>
0105 void CopyToGpu(Type const *const src, Type *const tgt, const unsigned size)
0106 {
0107   VECGEOM_DEVICE_API_CALL(Memcpy(tgt, src, size, VECGEOM_DEVICE_API_SYMBOL(MemcpyHostToDevice)));
0108 }
0109 
0110 template <typename Type>
0111 void CopyToGpu(Type const *const src, Type *const tgt)
0112 {
0113   CopyToGpu<Type>(src, tgt, sizeof(Type));
0114 }
0115 
0116 inline void CopyFromGpu(void const *const src, void *const tgt, const unsigned size)
0117 {
0118   VECGEOM_DEVICE_API_CALL(Memcpy(tgt, src, size, VECGEOM_DEVICE_API_SYMBOL(MemcpyDeviceToHost)));
0119 }
0120 
0121 class DevicePtrBase {
0122   void *fPtr;
0123 #ifdef DEBUG_DEVICEPTR
0124   size_t fAllocatedSize;
0125   bool fIncremented;
0126 #endif
0127 
0128 protected:
0129   DevicePtrBase(const DevicePtrBase &orig)
0130       : fPtr(orig.fPtr)
0131 #ifdef DEBUG_DEVICEPTR
0132         ,
0133         fAllocatedSize(0), fIncremented(false)
0134 #endif
0135   {
0136   }
0137 
0138   DevicePtrBase &operator=(const DevicePtrBase &orig)
0139   {
0140     fPtr = orig.fPtr;
0141 #ifdef DEBUG_DEVICEPTR
0142     fAllocatedSize = orig.fAllocatedSize;
0143     fIncremented   = orig.fIncremented;
0144 #endif
0145     return *this;
0146   }
0147 
0148   void MemcpyToDevice(const void *what, unsigned long nbytes)
0149   {
0150     VECGEOM_DEVICE_API_CALL(Memcpy(fPtr, what, nbytes, VECGEOM_DEVICE_API_SYMBOL(MemcpyHostToDevice)));
0151   }
0152 
0153   void MemcpyToHostAsync(void *where, unsigned long nbytes, cudaStream_t stream)
0154   {
0155     VECGEOM_DEVICE_API_CALL(MemcpyAsync(where, fPtr, nbytes, VECGEOM_DEVICE_API_SYMBOL(MemcpyDeviceToHost), stream));
0156   }
0157 
0158   VECCORE_ATT_HOST_DEVICE
0159   void *GetPtr() const { return fPtr; }
0160 
0161   void Free()
0162   {
0163     VECGEOM_DEVICE_API_CALL(Free((void *)fPtr));
0164 #ifdef DEBUG_DEVICEPTR
0165     fAllocatedSize = 0;
0166 #endif
0167   }
0168 
0169   void Increment(long add)
0170   {
0171     fPtr = (char *)fPtr + add;
0172 #ifdef DEBUG_DEVICEPTR
0173     if (add) fIncremented = true;
0174 #endif
0175   }
0176 
0177 public:
0178   DevicePtrBase()
0179       : fPtr(0)
0180 #ifdef DEBUG_DEVICEPTR
0181         ,
0182         fAllocatedSize(0), fIncremented(0)
0183 #endif
0184   {
0185   }
0186 
0187   explicit DevicePtrBase(void *input)
0188       : fPtr(input)
0189 #ifdef DEBUG_DEVICEPTR
0190         ,
0191         fAllocatedSize(0), fIncremented(0)
0192 #endif
0193   {
0194   }
0195 
0196   ~DevicePtrBase() { /* does not own content per se */ }
0197 
0198   void Malloc(unsigned long size)
0199   {
0200     VECGEOM_DEVICE_API_CALL(Malloc((void **)&fPtr, size));
0201 #ifdef DEBUG_DEVICEPTR
0202     fAllocatedSize = size;
0203 #endif
0204   }
0205 };
0206 
0207 template <typename T>
0208 class DevicePtr;
0209 
0210 template <typename Type, typename Derived = DevicePtr<Type>>
0211 class DevicePtrImpl : public DevicePtrBase {
0212 protected:
0213   DevicePtrImpl(const DevicePtrImpl & /* orig */)          = default;
0214   DevicePtrImpl &operator=(const DevicePtrImpl & /*orig*/) = default;
0215   DevicePtrImpl()                                          = default;
0216   explicit DevicePtrImpl(void *input) : DevicePtrBase(input) {}
0217   ~DevicePtrImpl() = default;
0218 
0219 public:
0220   void Allocate(unsigned long nelems = 1) { Malloc(nelems * Derived::SizeOf()); }
0221 
0222   void Deallocate() { Free(); }
0223 
0224   void ToDevice(const Type *what, unsigned long nelems = 1) { MemcpyToDevice(what, nelems * Derived::SizeOf()); }
0225   void FromDevice(Type *where, cudaStream_t stream)
0226   {
0227     // Async since we pass a stream.
0228     MemcpyToHostAsync(where, Derived::SizeOf(), stream);
0229   }
0230   void FromDevice(Type *where, unsigned long nelems, cudaStream_t stream)
0231   {
0232     // Async since we pass a stream.
0233     MemcpyToHostAsync(where, nelems * Derived::SizeOf(), stream);
0234   }
0235 
0236   VECCORE_ATT_HOST_DEVICE
0237   Type *GetPtr() const { return reinterpret_cast<Type *>(DevicePtrBase::GetPtr()); }
0238 
0239   VECCORE_ATT_HOST_DEVICE
0240   operator Type *() const { return GetPtr(); }
0241 
0242   Derived &operator++() // prefix ++
0243   {
0244     Increment(Derived::SizeOf());
0245     return *(Derived *)this;
0246   }
0247 
0248   Derived operator++(int) // postfix ++
0249   {
0250     Derived tmp(*(Derived *)this);
0251     Increment(Derived::SizeOf());
0252     return tmp;
0253   }
0254 
0255   Derived operator+(const size_t &rhs)
0256   {
0257     Derived tmp(*(Derived *)this);
0258     tmp.Increment(rhs);
0259     return tmp;
0260   }
0261 
0262   Derived &operator+=(long len) // prefix ++
0263   {
0264     Increment(len * Derived::SizeOf());
0265     return *(Derived *)this;
0266   }
0267 };
0268 
0269 template <typename Type>
0270 class DevicePtr : public DevicePtrImpl<Type> {
0271 public:
0272   DevicePtr()                                 = default;
0273   DevicePtr(const DevicePtr &)                = default;
0274   DevicePtr &operator=(const DevicePtr &orig) = default;
0275 
0276   // should be taking a DevicePtr<void*>
0277   explicit DevicePtr(void *input) : DevicePtrImpl<Type>(input) {}
0278 
0279   // Need to go via the explicit route accepting all conversion
0280   // because the regular c++ compilation
0281   // does not actually see the declaration for the cuda version
0282   // (and thus can not determine the inheritance).
0283   template <typename inputType>
0284   explicit DevicePtr(DevicePtr<inputType> const &input) : DevicePtrImpl<Type>((void *)input)
0285   {
0286   }
0287 
0288   // Disallow conversion from const to non-const.
0289   DevicePtr(DevicePtr<const Type> const &input,
0290             typename std::enable_if<!std::is_const<Type>::value, Type>::type * = nullptr) = delete;
0291 
0292 #ifdef VECCORE_CUDA
0293   // Allows implicit conversion from DevicePtr<Derived> to DevicePtr<Base>
0294   template <typename inputType, typename std::enable_if<std::is_base_of<Type, inputType>::value>::type * = nullptr>
0295   DevicePtr(DevicePtr<inputType> const &input) : DevicePtrImpl<Type>(input.GetPtr())
0296   {
0297   }
0298 
0299   // Disallow conversion from const to non-const.
0300   template <typename inputType, typename std::enable_if<std::is_base_of<Type, inputType>::value>::type * = nullptr>
0301   DevicePtr(DevicePtr<const inputType> const &input) = delete;
0302 #endif
0303 
0304 #ifdef VECCORE_CUDA
0305   template <typename... ArgsTypes>
0306   void Construct(ArgsTypes... params) const
0307   {
0308     ConstructOnGpu<<<1, 1>>>(this->GetPtr(), params...);
0309   }
0310 
0311   template <typename... ArgsTypes>
0312   void ConstructArray(size_t nElements, ArgsTypes... params) const
0313   {
0314     ConstructArrayOnGpu<<<nElements, 1>>>(this->GetPtr(), nElements, params...);
0315   }
0316 
0317   static size_t SizeOf() { return sizeof(Type); }
0318 
0319 #else
0320   template <typename... ArgsTypes>
0321   void Construct(ArgsTypes... params) const;
0322   template <typename... ArgsTypes>
0323   void ConstructArray(size_t nElements, ArgsTypes... params) const;
0324 
0325   static size_t SizeOf();
0326 #endif
0327 };
0328 
0329 template <typename Type>
0330 class DevicePtr<const Type> : private DevicePtrImpl<const Type> {
0331 public:
0332   DevicePtr()                                 = default;
0333   DevicePtr(const DevicePtr &)                = default;
0334   DevicePtr &operator=(const DevicePtr &orig) = default;
0335 
0336   // should be taking a DevicePtr<void*>
0337   explicit DevicePtr(void *input) : DevicePtrBase(input) {}
0338 
0339   // Need to go via the explicit route accepting all conversion
0340   // because the regular c++ compilation
0341   // does not actually see the declaration for the cuda version
0342   // (and thus can not determine the inheritance).
0343   template <typename inputType>
0344   explicit DevicePtr(DevicePtr<inputType> const &input) : DevicePtrImpl<const Type>((void *)input)
0345   {
0346   }
0347 
0348   // Implicit conversion from non-const to const.
0349   DevicePtr(DevicePtr<typename std::remove_const<Type>::type> const &input) : DevicePtrImpl<const Type>((void *)input)
0350   {
0351   }
0352 
0353 #ifdef VECCORE_CUDA
0354   // Allows implicit conversion from DevicePtr<Derived> to DevicePtr<Base>
0355   template <typename inputType, typename std::enable_if<std::is_base_of<Type, inputType>::value>::type * = nullptr>
0356   DevicePtr(DevicePtr<inputType> const &input) : DevicePtrImpl<const Type>(input.GetPtr())
0357   {
0358   }
0359 #endif
0360 
0361   VECCORE_ATT_HOST_DEVICE
0362   const Type *GetPtr() const { return reinterpret_cast<const Type *>(DevicePtrBase::GetPtr()); }
0363 
0364   VECCORE_ATT_HOST_DEVICE
0365   operator const Type *() const { return GetPtr(); }
0366 
0367 #ifdef VECCORE_CUDA
0368   template <typename DataClass, typename... ArgsTypes>
0369   void Construct(ArgsTypes... params) const
0370   {
0371     ConstructOnGpu<<<1, 1>>>(*(*this), params...);
0372   }
0373 
0374   template <typename... ArgsTypes>
0375   void ConstructArray(size_t nElements, ArgsTypes... params) const
0376   {
0377     ConstructArrayOnGpu<<<nElements, 1>>>(this->GetPtr(), nElements, params...);
0378   }
0379 
0380   static size_t SizeOf() { return sizeof(Type); }
0381 
0382 #else
0383   template <typename... ArgsTypes>
0384   void Construct(ArgsTypes... params) const;
0385   template <typename... ArgsTypes>
0386   void ConstructArray(size_t nElements, ArgsTypes... params) const;
0387 
0388   static size_t SizeOf();
0389 #endif
0390 };
0391 
0392 namespace CudaInterfaceHelpers {
0393 
0394 /*!
0395  * Copy multiple arrays of values to the GPU.
0396  * For each array, allocate memory on the device, and copy it to the GPU.
0397  * The cpuToGpuMapping finally maps the CPU array pointers to the GPU arrays.
0398  * \param[out] cpuToGpuMapping Mapping of CPU array to GPU array. It gets filled during the function execution.
0399  * \param[in]  nElement Number of elements in all collections.
0400  * \param[in]  toCopy First array to copy.
0401  * \param[in]  restToCopy Parameter pack with more arrays to copy (can be empty).
0402  */
0403 template <typename Arg_t, typename... Args_t>
0404 void allocateAndCopyToGpu(std::unordered_map<const void *, void *> &cpuToGpuMapping, std::size_t nElement,
0405                           const Arg_t *toCopy, const Args_t *...restToCopy)
0406 {
0407   const auto nByte         = sizeof(toCopy[0]) * nElement;
0408   const void *hostMem      = toCopy;
0409   void *deviceMem          = AllocateOnGpu<void *>(nByte);
0410   cpuToGpuMapping[hostMem] = deviceMem;
0411   CopyToGpu(hostMem, deviceMem, nByte);
0412 
0413   if constexpr (sizeof...(Args_t) > 0) {
0414     allocateAndCopyToGpu(cpuToGpuMapping, nElement, restToCopy...);
0415   }
0416 }
0417 
0418 } // namespace CudaInterfaceHelpers
0419 
0420 /*!
0421  * Construct many objects on the GPU, whose addresses and constructor parameters are passed as arrays.
0422  * \tparam DataClass The type to construct on the GPU.
0423  * \tparam DevPtr_t Device pointer type to specify the location of the GPU objects.
0424  * \param  nElement Number of elements to construct. It is assumed that all argument arrays have this length.
0425  * \param  gpu_ptrs Array of addresses to place the new objects at.
0426  * \param  params   Array(s) of constructor parameters with one entry for each object.
0427  */
0428 template <class DataClass, class DevPtr_t, typename... Args_t>
0429 void ConstructManyOnGpu(std::size_t nElement, const DevPtr_t *gpu_ptrs, const Args_t *...params)
0430 #ifdef VECCORE_CUDA
0431 {
0432   using namespace CudaInterfaceHelpers;
0433   std::unordered_map<const void *, void *> cpuToGpuMem;
0434   std::vector<DataClass *> raw_gpu_ptrs;
0435   std::transform(gpu_ptrs, gpu_ptrs + nElement, std::back_inserter(raw_gpu_ptrs),
0436                  [](const DevPtr_t &ptr) { return static_cast<DataClass *>(ptr.GetPtr()); });
0437   allocateAndCopyToGpu(cpuToGpuMem, nElement, raw_gpu_ptrs.data(), params...);
0438 
0439   ConstructManyOnGpu_kernel<<<128, 32>>>(raw_gpu_ptrs.size(),
0440                                          static_cast<decltype(raw_gpu_ptrs.data())>(cpuToGpuMem[raw_gpu_ptrs.data()]),
0441                                          static_cast<decltype(params)>(cpuToGpuMem[params])...);
0442 
0443   for (const auto &memCpu_memGpu : cpuToGpuMem) {
0444     FreeFromGpu(memCpu_memGpu.second);
0445   }
0446 
0447   VECGEOM_DEVICE_API_CALL(GetLastError());
0448 }
0449 #else
0450     ;
0451 #endif
0452 
0453 template <class DataClass, class DevPtr_t>
0454 void CopyBBoxesToGpuImpl(std::size_t nElement, const DevPtr_t *gpu_ptrs, Precision *boxes_data)
0455 #ifdef VECCORE_CUDA
0456 {
0457   std::unordered_map<const void *, void *> cpuToGpuMem;
0458   std::vector<DataClass *> raw_gpu_ptrs;
0459   std::transform(gpu_ptrs, gpu_ptrs + nElement, std::back_inserter(raw_gpu_ptrs),
0460                  [](const DevPtr_t &ptr) { return static_cast<DataClass *>(ptr.GetPtr()); });
0461 
0462   const auto nByteBoxes        = 6 * nElement * sizeof(Precision);
0463   const auto nByteVolumes      = nElement * sizeof(DataClass *);
0464   Precision *boxes_data_gpu    = AllocateOnGpu<Precision>(nByteBoxes);
0465   DataClass **raw_gpu_ptrs_gpu = AllocateOnGpu<DataClass *>(nByteVolumes);
0466 
0467   CopyToGpu(boxes_data, boxes_data_gpu, nByteBoxes);
0468   CopyToGpu(raw_gpu_ptrs.data(), raw_gpu_ptrs_gpu, nByteVolumes);
0469   VECGEOM_DEVICE_API_CALL(DeviceSynchronize());
0470 
0471   CopyBBoxesToGpu<DataClass><<<128, 32>>>(raw_gpu_ptrs.size(), raw_gpu_ptrs_gpu, boxes_data_gpu);
0472 
0473   FreeFromGpu(boxes_data_gpu);
0474   FreeFromGpu(raw_gpu_ptrs_gpu);
0475 }
0476 #else
0477     ;
0478 #endif
0479 
0480 } // End cxx namespace
0481 
0482 } // namespace vecgeom
0483 
0484 #endif // VECGEOM_ENABLE_CUDA
0485 
0486 #endif // VECGEOM_BACKEND_CUDA_INTERFACE_H_