Zip and Skip Tries
Loading...
Searching...
No Matches
cuda_utils.cuh
Go to the documentation of this file.
1
9#pragma once
10#include <bit> // bit_width
11#include <cstddef> // size_t
12#include <cstdint> // uintmax_t
13#include <cstdio> // fprintf
14#include <cuda_runtime.h> // CUDA runtime
15#include <limits> // std::numeric_limits
16
21#define BLOCKS 64
26#define THREADS 1024
27
35#define gpuErrchk(ans) { gpuAssert(ans, __FILE__, __LINE__); }
36
49inline void gpuAssert(cudaError_t code, const char *file, int line, bool abort = true)
50{
51 if (code != cudaSuccess)
52 {
53 fprintf(stderr, "GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
54
55 if (abort)
56 {
57 exit(code);
58 }
59 }
60}
61
66static constexpr bool CHECK_CUDA_ERRORS = false;
67
74__host__ inline size_t host_fast_log2(size_t n)
75{
76 // std::bit_width(n) returns number of bits needed to represent n.
77 // If n is power of 2, say 8 (1000), bit_width is 4. log2(8) = 3. So bit_width - 1.
78 // If n is not power of 2, say 7 (0111), bit_width is 3. log2(7) is approx 2.8. floor(log2(7)) = 2. So bit_width - 1.
79 return std::bit_width(n) - 1;
80}
81
88__host__ inline size_t host_fast_log2_ceil(size_t n)
89{
90 // std::bit_width(n-1) returns number of bits needed for n-1.
91 // If n is power of 2, say 8 (1000), n-1 is 7 (0111). bit_width(7) = 3. ceil(log2(8)) = 3.
92 // If n is not power of 2, say 7 (0111), n-1 is 6 (0110). bit_width(6) = 3. ceil(log2(7)) = 3.
93 // Handles n=1 case correctly: n-1=0, bit_width(0)=0. ceil(log2(1))=0.
94 return std::bit_width(n - 1);
95}
96
103__device__ __forceinline__ size_t device_fast_log2(size_t n)
104{
105 // For size_t (unsigned long long on many 64-bit systems), digits is 64.
106 // __clzll(n) counts leading zeros.
107 // If n=8 (0...01000), clzll=60. digits-1-clzll = 64-1-60 = 3. log2(8)=3.
108 // If n=7 (0...00111), clzll=61. digits-1-clzll = 64-1-61 = 2. floor(log2(7))=2.
109 return std::numeric_limits<size_t>::digits - 1 - __clzll(n);
110}
111
118__device__ __forceinline__ size_t device_fast_log2_ceil(size_t n)
119{
120 // If n=8 (0...01000), n-1=7 (0...0111). clzll(7)=61. digits-clzll(n-1) = 64-61 = 3. ceil(log2(8))=3.
121 // If n=7 (0...00111), n-1=6 (0...0110). clzll(6)=61. digits-clzll(n-1) = 64-61 = 3. ceil(log2(7))=3.
122 // Handles n=1: n-1=0. clzll(0) is undefined but often returns digits (64). digits-digits = 0. ceil(log2(1))=0.
123 return std::numeric_limits<size_t>::digits - __clzll(n - 1);
124}
125
133{
134public:
139 {
140 cudaEventCreate(&start);
141 cudaEventCreate(&stop);
142 }
143
148 {
149 cudaEventDestroy(start);
150 cudaEventDestroy(stop);
151 }
152
157 {
158 cudaEventRecord(start);
159 }
160
167 {
168 cudaEventRecord(stop);
169 cudaEventSynchronize(stop); // Wait for the stop event to complete
170 cudaEventElapsedTime(&time, start, stop); // Calculate time difference
171 return time;
172 }
173
174private:
175 cudaEvent_t start, stop;
176 float time;
177};
178
189template <typename T>
190inline T* alloc_to_device(size_t size)
191{
192 T* d_arr;
193
194 if constexpr (CHECK_CUDA_ERRORS)
195 {
196 gpuErrchk(cudaMalloc(&d_arr, size * sizeof(T)));
197 }
198 else
199 {
200 cudaMalloc(&d_arr, size * sizeof(T));
201 }
202
203 return d_arr;
204}
205
218template <typename T>
219inline T* copy_to_device(T* d_arr, const T* arr, size_t size)
220{
221 if constexpr (CHECK_CUDA_ERRORS)
222 {
223 gpuErrchk(cudaMemcpy(d_arr, arr, size * sizeof(T), cudaMemcpyHostToDevice));
224 }
225 else
226 {
227 cudaMemcpy(d_arr, arr, size * sizeof(T), cudaMemcpyHostToDevice);
228 }
229
230 return d_arr;
231}
232
244template <typename T>
245inline T* copy_to_device(const T* arr, size_t size)
246{
247 T* d_arr = alloc_to_device<T>(size);
248 copy_to_device(d_arr, arr, size);
249 return d_arr;
250}
251
264template <typename T>
265inline T* memset_within_device(T* d_arr, size_t size, int value)
266{
267 if constexpr (CHECK_CUDA_ERRORS)
268 {
269 gpuErrchk(cudaMemset(d_arr, value, size * sizeof(T)));
270 }
271 else
272 {
273 cudaMemset(d_arr, value, size * sizeof(T));
274 }
275
276 return d_arr;
277}
278
290template <typename T>
291inline T* memset_to_device(size_t size, int value)
292{
293 T* d_arr = alloc_to_device<T>(size);
294 memset_within_device(d_arr, size, value);
295 return d_arr;
296}
297
310template <typename T>
311inline T* copy_within_device(T* d_dest, const T* d_src, size_t size)
312{
313 if constexpr (CHECK_CUDA_ERRORS)
314 {
316 }
317 else
318 {
320 }
321
322 return d_dest;
323}
324
334{
335 if constexpr (CHECK_CUDA_ERRORS)
336 {
338 }
339 else
340 {
342 }
343}
344
358template <typename T>
359inline T* copy_from_device(const T* d_arr, size_t size)
360{
361 T* arr = new T[size]; // Allocate host memory
362
363 if constexpr (CHECK_CUDA_ERRORS)
364 {
366 }
367 else
368 {
369 cudaMemcpy(arr, d_arr, size * sizeof(T), cudaMemcpyDeviceToHost);
370 }
371
372 return arr;
373}
374
384template <typename T>
385inline void device_free(T* d_arr)
386{
387 if constexpr (CHECK_CUDA_ERRORS)
388 {
390 }
391 else
392 {
394 }
395}
396
407{
408 return threadIdx.x + blockIdx.x * blockDim.x;
409}
410
411// Very marginally slower (about 0.5% than get_num_threads() below)
412// __device__ __forceinline__ size_t get_num_threads() {
413// return blockDim.x * gridDim.x;
414// }
415
416
424constexpr size_t get_num_threads()
425{
426 return BLOCKS * THREADS;
427}
A simple CUDA timer class using CUDA events.
~CudaTimer()
Destroys the CUDA events.
cudaEvent_t start
cudaEvent_t stop
CUDA events to mark start and stop points.
float stop_timer()
Records the stop event, synchronizes, and calculates elapsed time.
float time
Stores the calculated elapsed time in milliseconds.
void start_timer()
Records the start event in the default CUDA stream.
CudaTimer()
Constructs the CudaTimer and creates CUDA events.
static constexpr bool CHECK_CUDA_ERRORS
Compile-time flag to enable/disable CUDA error checking via gpuErrchk/gpuAssert.
constexpr size_t get_num_threads()
Gets the total number of threads in the kernel launch (compile-time).
#define THREADS
Default number of threads per block in CUDA kernels.
#define gpuErrchk(ans)
Macro to check CUDA API calls for errors.
void synchronize_device()
Synchronizes the host thread with the default CUDA device stream.
__device__ __forceinline__ size_t device_fast_log2(size_t n)
Computes the floor of log base 2 for a device integer using count leading zeros.
T * memset_to_device(size_t size, int value)
Allocates device memory and sets it to a specific byte value.
__device__ __forceinline__ size_t device_fast_log2_ceil(size_t n)
Computes the ceiling of log base 2 for a device integer using count leading zeros.
T * memset_within_device(T *d_arr, size_t size, int value)
Sets a region of device memory to a specific byte value.
T * copy_within_device(T *d_dest, const T *d_src, size_t size)
Copies data from one device memory location to another.
__host__ size_t host_fast_log2_ceil(size_t n)
Computes the ceiling of log base 2 for a host integer using std::bit_width.
T * copy_from_device(const T *d_arr, size_t size)
Allocates host memory and copies data from device memory to it.
void gpuAssert(cudaError_t code, const char *file, int line, bool abort=true)
Asserts if a CUDA error code indicates failure.
void device_free(T *d_arr)
Frees memory on the CUDA device.
T * copy_to_device(T *d_arr, const T *arr, size_t size)
Copies data from host memory to existing device memory.
__device__ __forceinline__ size_t get_tid()
Gets the global thread ID within a CUDA kernel launch.
#define BLOCKS
Default number of blocks to launch in CUDA kernels.
T * alloc_to_device(size_t size)
Allocates memory on the CUDA device.
__host__ size_t host_fast_log2(size_t n)
Computes the floor of log base 2 for a host integer using std::bit_width.
@ T
Thymine.