compute-acceleration
GEMM and sum reduction across serial, OpenMP and CUDA
Loading...
Searching...
No Matches
cuda_check.cuh
1// Copyright (c) 2025 yanghuafang
2// SPDX-License-Identifier: MIT
3
4#ifndef ACCEL_CUDA_CUDA_CHECK_CUH_
5#define ACCEL_CUDA_CUDA_CHECK_CUH_
6
7#include <stdexcept>
8#include <string>
9
10#include <cuda_runtime.h>
11
12namespace accel {
13
14// Exception carrying a failed CUDA runtime status.
15//
16// Carrying the failure as an exception rather than the usual `printf` plus
17// `exit(1)` is what makes the RAII owners in this project work: an `exit` from
18// inside a check macro bypasses every destructor on the stack and leaks the
19// device allocations they hold. Throwing lets them release first.
20class CudaError : public std::runtime_error {
21 public:
22 CudaError(cudaError_t code, const std::string& what)
23 : std::runtime_error(what), code_(code) {}
24
25 // The originating `cudaError_t`, for callers that branch on
26 // `cudaErrorMemoryAllocation` and retry with a smaller working set.
27 cudaError_t code() const noexcept { return code_; }
28
29 private:
30 cudaError_t code_;
31};
32
33namespace detail {
34
35inline std::string FormatCudaError(cudaError_t code, const char* expression,
36 const char* file, int line) {
37 return std::string(file) + ':' + std::to_string(line) + ": " + expression +
38 " failed with " + cudaGetErrorName(code) + " (" +
39 cudaGetErrorString(code) + ')';
40}
41
42// Out-of-line-ish helper so the macro expands to a single expression and stays
43// usable in any statement position.
44inline void CheckCudaStatus(cudaError_t code, const char* expression,
45 const char* file, int line) {
46 if (code != cudaSuccess) {
47 throw CudaError(code, FormatCudaError(code, expression, file, line));
48 }
49}
50
51} // namespace detail
52} // namespace accel
53
54// Evaluates a CUDA runtime call and throws accel::CudaError on failure.
55//
56// Never use inside a destructor or a `noexcept` function.
57#define CUDA_CHECK(expression) \
58 ::accel::detail::CheckCudaStatus((expression), #expression, __FILE__, \
59 __LINE__)
60
61// Validates a kernel launch and waits for it to retire.
62//
63// Two distinct failures need catching after a launch: `cudaGetLastError()`
64// reports configuration errors raised synchronously at launch time, while
65// `cudaDeviceSynchronize()` surfaces faults the kernel hit while executing.
66// Checking only the former lets an out-of-bounds store go unreported until
67// some unrelated later call inherits the sticky error.
68//
69// Synchronising is a benchmarking and debugging tool; do not place this inside
70// a timed loop that is meant to overlap work.
71#define CUDA_CHECK_LAUNCH() \
72 do { \
73 CUDA_CHECK(cudaGetLastError()); \
74 CUDA_CHECK(cudaDeviceSynchronize()); \
75 } while (0)
76
77#endif // ACCEL_CUDA_CUDA_CHECK_CUH_