在cuda

时间:2016-06-30 21:09:15

标签: c++ templates cuda parameter-passing

我正在尝试在cuda中编写一个简化函数(这是一个练习,我知道我正在做的事情已被其他人做得更好),它采用二进制关联运算符和数组并减少数组使用运营商。

我在如何传递这个功能方面遇到了困难。我已经将hostOp()编写为基于主机的示例,它可以正常工作。

deviceOp()通过显式调用fminf()来处理第一个语句,但是当我调用function参数时,存在非法的内存访问错误。

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std; //for brevity

__device__  float g_d_a = 9, g_d_b = 5;
float g_h_a = 9, g_h_b = 5;

template<typename argT, typename funcT>
__global__
void deviceOp(funcT op){    
    argT result = fminf(g_d_a, g_d_b);                  //works fine
    printf("static function result: %f\n", result);
    result = op(g_d_a,g_d_b);                           //illegal memory access
    printf("template function result: %f\n", result);
}

template<typename argT, typename funcT>                 
void hostOp(funcT op){
    argT result = op(g_h_a, g_h_b);
    printf("template function result: %f\n", result);
}

int main(int argc, char* argv[]){
    hostOp<float>(min<float>);                          //works fine
    deviceOp<float><<<1,1>>>(fminf);

    cudaDeviceSynchronize(); 
    cout<<cudaGetErrorString(cudaGetLastError())<<endl;
}

输出:

host function result: 5.000000
static function result: 5.000000
an illegal memory access was encountered

假设我没有做一些非常愚蠢的事情,我应该如何将fminf传递给deviceOp,以便没有非法的内存访问?

如果我做的事情非常愚蠢,那么更好的方法是什么?

1 个答案:

答案 0 :(得分:1)

要在设备上调用的函数必须使用__device__(或__global__,如果您希望它是内核)进行修饰。 nvcc编译器驱动程序将分离主机和设备代码,并在从设备代码调用(即编译)时使用设备编译的函数版本,否则将使用主机版本。

这个结构存在问题:

deviceOp<float><<<1,1>>>(fminf);

虽然可能并不明显,但这基本上都是主机代码。是的,它是启动内核(通过主机代码的库调用的基础序列),但它在技术上是主机代码。因此,fminf功能地址&#34;已捕获&#34;这里将是fminf函数的主机版本,即使设备版本可用(通过CUDA math.h,您实际上并不包括)。

解决这个问题的一种典型虽然笨拙的方法是捕捉&#34;设备代码中的设备地址 ,然后将其作为参数传递给内核。

如果要传递可在编译时推导出的函数地址,您也可以(稍微)将此过程短路,并使用稍微不同的模板技术。 this answer中介绍了这些概念。

以下是使用设备代码中的&#34;捕获功能地址修改的代码的完整工作示例&#34;方法:

$ cat t1176.cu
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std; //for brevity

__device__  float g_d_a = 9, g_d_b = 5;
float g_h_a = 9, g_h_b = 5;

template<typename argT, typename funcT>
__global__
void deviceOp(funcT op){
    argT result = fminf(g_d_a, g_d_b);                  //works fine
    printf("static function result: %f\n", result);
    result = op(g_d_a,g_d_b);                           //illegal memory access
    printf("template function result: %f\n", result);
}

__device__ float (*my_fminf)(float, float) = fminf;  // "capture" device function address

template<typename argT, typename funcT>
void hostOp(funcT op){
    argT result = op(g_h_a, g_h_b);
    printf("template function result: %f\n", result);
}

int main(int argc, char* argv[]){
    hostOp<float>(min<float>);                          //works fine
    float (*h_fminf)(float, float);
    cudaMemcpyFromSymbol(&h_fminf, my_fminf, sizeof(void *));
    deviceOp<float><<<1,1>>>(h_fminf);

    cudaDeviceSynchronize();
    cout<<cudaGetErrorString(cudaGetLastError())<<endl;
}
$ nvcc -o t1176 t1176.cu
$ cuda-memcheck ./t1176
========= CUDA-MEMCHECK
template function result: 5.000000
static function result: 5.000000
template function result: 5.000000
no error
========= ERROR SUMMARY: 0 errors
$