blob: 4a238c404b0054dad90daa4393787216a9cf3704 [file] [edit]
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// SPDX-FileCopyrightText: The Eigen Authors
// SPDX-License-Identifier: MPL-2.0
// A failed runtime call or kernel launch inside GpuDevice has to reach EIGEN_GPU_RUNTIME_CHECK, in a release build
// as much as in a debug one (tensor_gpu_runtime_check_ndebug.cu compiles this file with EIGEN_NO_DEBUG). The macro
// is overridden here to record the failure instead of stopping the process.
#define EIGEN_TEST_NO_LONGDOUBLE
#define EIGEN_TEST_NO_COMPLEX
#define EIGEN_USE_GPU
#include <string>
struct RecordedGpuError {
int code = 0;
std::string expression;
std::string file;
int line = 0;
};
static RecordedGpuError g_last_gpu_error;
static int g_num_gpu_errors = 0;
// cudaSuccess and hipSuccess are both 0.
template <typename Error>
__host__ void record_gpu_runtime_error(Error error, const char* expression, const char* file, int line) {
if (static_cast<int>(error) == 0) return;
++g_num_gpu_errors;
g_last_gpu_error.code = static_cast<int>(error);
g_last_gpu_error.expression = expression;
g_last_gpu_error.file = file;
g_last_gpu_error.line = line;
}
#define EIGEN_GPU_RUNTIME_CHECK(expr) ::record_gpu_runtime_error((expr), #expr, __FILE__, __LINE__)
#include "main.h"
#include <contrib/Eigen/Tensor>
#if defined(EIGEN_USE_HIP)
static const int kInvalidConfiguration = static_cast<int>(hipErrorInvalidConfiguration);
static const int kInvalidValue = static_cast<int>(hipErrorInvalidValue);
#else
static const int kInvalidConfiguration = static_cast<int>(cudaErrorInvalidConfiguration);
static const int kInvalidValue = static_cast<int>(cudaErrorInvalidValue);
#endif
__global__ void write_one_kernel(int* out) {
if (blockIdx.x == 0 && threadIdx.x == 0) *out = 1;
}
static bool ends_with(const std::string& s, const std::string& suffix) {
return s.size() >= suffix.size() && s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;
}
// A valid launch through LAUNCH_GPU_KERNEL runs and records nothing; one with more threads per block than any
// device allows is refused at enqueue time, and the refusal is reported from gpu_launch's own check.
static void test_launch_check() {
Eigen::GpuStreamDevice stream;
Eigen::GpuDevice device(&stream);
int* d_out = static_cast<int*>(device.allocate(sizeof(int)));
device.memset(d_out, 0, sizeof(int));
g_num_gpu_errors = 0;
LAUNCH_GPU_KERNEL(write_one_kernel, 1, 32, 0, device, d_out);
int out = 0;
device.memcpyDeviceToHost(&out, d_out, sizeof(int));
device.synchronize();
VERIFY_IS_EQUAL(out, 1);
VERIFY_IS_EQUAL(g_num_gpu_errors, 0);
const int too_many_threads = 2 * device.maxGpuThreadsPerBlock();
LAUNCH_GPU_KERNEL(write_one_kernel, 1, too_many_threads, 0, device, d_out);
VERIFY_IS_EQUAL(g_num_gpu_errors, 1);
// Which of the two codes an oversize block gets depends on the toolkit: CUDA 13.3 reports invalid value.
VERIFY(g_last_gpu_error.code == kInvalidConfiguration || g_last_gpu_error.code == kInvalidValue);
VERIFY_IS_EQUAL(g_last_gpu_error.expression, std::string("gpuGetLastError()"));
VERIFY(ends_with(g_last_gpu_error.file, "GpuRuntime.h"));
device.deallocate(d_out);
VERIFY_IS_EQUAL(g_num_gpu_errors, 1);
}
// A runtime call that fails inside GpuDevice is reported with the call's own text and location.
static void test_runtime_call_check() {
Eigen::GpuStreamDevice stream;
Eigen::GpuDevice device(&stream);
g_num_gpu_errors = 0;
int host_dst = 0;
// HIP can accept a host source here; a null source with a nonzero size is invalid on both runtimes.
device.memcpyDeviceToHost(&host_dst, nullptr, sizeof(int));
VERIFY_IS_EQUAL(g_num_gpu_errors, 1);
VERIFY_IS_EQUAL(g_last_gpu_error.code, kInvalidValue);
VERIFY(g_last_gpu_error.expression.find("gpuMemcpyAsync") == 0);
VERIFY(ends_with(g_last_gpu_error.file, "TensorDeviceGpu.h"));
VERIFY(g_last_gpu_error.line > 0);
}
EIGEN_DECLARE_TEST(tensor_gpu_runtime_check) {
CALL_SUBTEST(test_launch_check());
CALL_SUBTEST(test_runtime_call_check());
}