【发布时间】:2019-11-22 01:24:08
【问题描述】:
我在 CUDA 内核代码中实现 lambda 表达式时遇到问题,它可以编译但执行失败。我使用 Ubuntu 18.04 和 CUDA 10.1 并使用 --expt-extended-lambda 编译。
我只是想以简洁明了的方式在自定义设备矩阵上实现一些基本操作,如点乘、加法、提取等。
我已经测试了推力,但它在与更复杂的自定义 CUDA 内核代码混合时导致了几个设备内存错误。手动 CUDA 分配内存,将 cudaMalloc 转换为 thrust::device_ptr,然后使用推力例程效果不佳,我宁愿摆脱推力。
这是一个失败的模板表达式的基本使用示例,我不知道为什么。 transform / transformProcess 方法失败。显然 binaryFunction 传递的 lambda 表达式不能应用于设备代码。
EDIT 2(修正代码没有编译错误)
Types.cuh
#ifndef TEST_CUDA_DEVICE_LAMBDA_PROCESSING_TYPES_CUH
#define TEST_CUDA_DEVICE_LAMBDA_PROCESSING_TYPES_CUH
#include <cuda_runtime.h>
#include <nvfunctional>
#include <iostream>
#include <vector>
#include <string>
typedef unsigned int uint;
inline bool check(int e, int iLine, const char *szFile) {
if (e < 0) {
std::cerr << "General error " << e << " at line " << iLine << " in file " << szFile << std::endl;
return false;
}
return true;
}
#define ck(call) check(call, __LINE__, __FILE__)
template <typename precision>
struct CudaMatrix {
typedef nvstd::function<precision(precision, precision)> binaryFunction;
CudaMatrix(uint width, uint height) : width(width), height(height) { }
__device__ __host__ uint size() const { return width * height; }
uint bytesSize() const { return size() * sizeof(precision); }
void fill(precision value);
void setValuesFromVector(const std::vector<precision> &vector);
void display(const std::string &name = "") const;
CudaMatrix transform(const CudaMatrix &A, binaryFunction lambda);
CudaMatrix operator+=(const CudaMatrix &m) { return transform(m, [=] __device__ (precision x, precision y) { return x + y; }); }
CudaMatrix operator-=(const CudaMatrix &m) { return transform(m, [=] __device__ (precision x, precision y) { return x - y; }); }
CudaMatrix operator*=(const CudaMatrix &m) { return transform(m, [=] __device__ (precision x, precision y) { return x * y; }); }
precision *data;
uint width,
height;
};
#endif //TEST_CUDA_DEVICE_LAMBDA_PROCESSING_TYPES_CUH
Types.cu
#include "Types.cuh"
/**
* Device code to set a matrix value to the given one
*
* @tparam precision - The matrix precision
*
* @param matrix - The matrix to set the value to
* @param value - The value to set
*/
template <typename precision>
__global__ void fillProcess(CudaMatrix<precision> matrix, precision value)
{
int x = blockDim.x * blockIdx.x + threadIdx.x;
if (x >= matrix.size()) {
return;
}
*(matrix.data + x) = value;
}
/**
* Device code to apply a function f for each element of matrix A and B with A = f(A, B)
*
* @tparam precision - The matrix precision
*
* @param A - The matrix A to store the result in
* @param B - The matrix B to compute the result from
* @param transform - The function to apply on each A'elements such as A(i) = transform(A(i), B(i))
*/
template<typename precision>
__global__ void transformProcess( CudaMatrix<precision> A,
CudaMatrix<precision> B,
const typename CudaMatrix<precision>::binaryFunction &transform
) {
int x = blockDim.x * blockIdx.x + threadIdx.x;
if (x >= A.size()) {
return;
}
*(A.data + x) = transform(*(A.data + x), *(B.data + x));
}
/**
* Display the matrix
*
* @tparam precision - The matrix precision
*
* @param name - The matrix name
*/
template <typename precision>
void CudaMatrix<precision>::display(const std::string &name) const
{
precision *hostValues;
ck(cudaMallocHost(&hostValues, bytesSize()));
ck(cudaMemcpy(hostValues, data, bytesSize(), cudaMemcpyDeviceToHost));
std::cout << "Matrix " << name << " " << width << " x " << height << " pixels of " << typeid(precision).name()
<< "\n\n";
for (int i = 0; i < height; ++i) {
std::cout << "{ ";
for (int j = 0; j < width - 1; ++j) {
std::cout << *(hostValues + i * width + j) << ", ";
}
std::cout << *(hostValues + (i + 1) * width - 1) << " }\n";
}
std::cout << std::endl;
ck(cudaFreeHost(hostValues));
}
/**
* Fill the matrix with the given value
*
* @tparam precision - The matrix precision
*
* @param value - The value to set all matrix's elements with
*/
template <typename precision>
void CudaMatrix<precision>::fill(precision value)
{
const uint threadsPerBlock = 128;
const uint numBlock = size() / threadsPerBlock + 1;
fillProcess<<< numBlock, threadsPerBlock >>>(*this, value);
}
/**
* Set the matrix values in device CUDA memory from a host standard vector
*
* @param vector - The values to set
*/
template <typename precision>
void CudaMatrix<precision>::setValuesFromVector(const std::vector<precision> &vector)
{
ck(cudaMemcpy(data, vector.data(), vector.size() * sizeof(precision), cudaMemcpyHostToDevice));
}
/**
* Apply the function "fn" to all elements of the current matrix such as *this[i] = fn(*this[i], A[i])
*
* @tparam precision - The matrix precision
*
* @param A - The input matrix A
* @param op - The binary function to apply
*
* @return This
*/
template<typename precision>
CudaMatrix<precision> CudaMatrix<precision>::transform(const CudaMatrix &A, binaryFunction fn)
{
const uint threadsPerBlock = 128;
const uint numBlock = size() / threadsPerBlock + 1;
transformProcess<<< numBlock, threadsPerBlock >>>(*this, A, fn);
return *this;
}
// Forward template declarations
template struct CudaMatrix<double>;
template struct CudaMatrix<float>;
template struct CudaMatrix<int>;
main.cpp
#include "Types.cuh"
int main(int argc, char **argv)
{
// Allocate memory
CudaMatrix<double> m1(3, 3);
CudaMatrix<double> m2(3, 3);
ck(cudaMalloc(&m1.data, m1.bytesSize()));
ck(cudaMalloc(&m2.data, m2.bytesSize()));
// Test here
m1.setValuesFromVector({1, 1, 1, 2, 2, 2, 3, 3, 3});
m2.fill(10);
m1.display("m1");
m2.display("m2");
m1 *= m2;
m1.display("m1 * m2");
m1 += m2;
m1.display("m1 + m2");
// Clean memory
ck(cudaFree(m1.data));
ck(cudaFree(m2.data));
return EXIT_SUCCESS;
}
输出
Matrix m1 3 x 3 pixels of d
{ 1, 1, 1 }
{ 2, 2, 2 }
{ 3, 3, 3 }
Matrix m2 3 x 3 pixels of d
{ 10, 10, 10 }
{ 10, 10, 10 }
{ 10, 10, 10 }
Matrix m1 * m2 3 x 3 pixels of d
{ 1, 1, 1 }
{ 2, 2, 2 }
{ 3, 3, 3 }
Matrix m1 + m2 3 x 3 pixels of d
Segmentation fault (core dumped)
编辑 3
Robert Crovella 的“嵌套”模板策略解决方案运行良好。
【问题讨论】:
-
您有实际问题要问吗?作为备注,CUDA GPU 操作不会导致主机端分段错误。这将需要诸如越界主机内存访问或冲洗堆栈之类的东西。因此,(a)您要问什么以及(b)您显示的段错误与您发布的 CUDA 代码有什么关系并不明显
-
您好,我认为segfault是由于内核函数
transformProcess执行的操作对设备内存的非法操作造成的。问题不在于段错误,而是应用给定 lambda 函数导致此段错误的原因。这条线*(A.data + x) = transform(*(A.data + x), *(B.data + x));失败了,我问为什么。预期结果是A(i) = A(i) + B(i)。这是我提出的问题,段错误只是这种不当行为的结果,这显然是由于修改设备内存的 CUDA 内核代码造成的。 -
参见项目 1 here。如果你问为什么你的代码不能工作,你应该提供一个minimal reproducible example。到目前为止,您所展示的不是一个。我已投票结束您的问题。
-
您是否真的尝试精确编译并且只编译您在此处显示的内容?因为那是我试图做的。它没有用。如果你尝试一下,你也会发现它不起作用。在我向您的
Types.cuh添加了大约 4 或 5 项所需的东西之后,我最终得到了setValuesFromVector的缺失函数实现。是的,我可能也可以解决这个问题。如果您认为我应该做所有这些来尝试运行您的代码,那么对不起,我不同意您的观点。我相信声称您的代码不完整是一个非常可辩护的声明。 -
在我看来,没有必要在这个问题中提供任何进一步的信息。我已经根据您的代码创建了一个工作示例,并解决了您代码中的问题,并为您提供了解决方案。您问题最后一段中的主题已得到解释和修复。如果您的问题仍未以某种方式得到解答,我的建议是创建一个新问题。