gpt4 book ai didi

c++ - CUDA 7.5 实验性 __host__ __device__ lambda

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:38:41 36 4
gpt4 key购买 nike

我玩了一下 experimental device lambdas在 CUDA 7.5 中引入并在此 blog post by Mark Harris 中提升.

对于下面的示例,我删除了很多不需要显示我的问题的东西(我的实际实现看起来更好......)。

我尝试编写一个 foreach 函数,它根据模板参数对设备上的 vector (每个元素 1 个线程)或主机(串行)进行操作。使用这个 foreach 函数,我可以轻松实现 BLAS 函数。例如,我使用为 vector 的每个分量分配一个标量(我在最后附上完整的代码):

template<bool onDevice> void assignScalar( size_t size, double* vector, double a )
{
auto assign = [=] __host__ __device__ ( size_t index ) { vector[index] = a; };
if( onDevice )
{
foreachDevice( size, assign );
}
else
{
foreachHost( size, assign );
}
}

但是,由于 __host__ __device__,这段代码给出了一个编译器错误。 lambda :

The closure type for a lambda ("lambda ->void") cannot be used in the template argument type of a __global__ function template instantiation, unless the lambda is defined within a __device__ or __global__ function

如果删除 __device__,我会得到同样的错误来自 lambda 表达式,如果我删除 __host__,我不会得到编译错误(仅 __device__ lambda),但在这种情况下,主机部分未执行...

如果我将 lambda 定义为 __host____device__单独地,代码编译并按预期工作。

template<bool onDevice> void assignScalar2( size_t size, double* vector, double a )
{
if( onDevice )
{
auto assign = [=] __device__ ( size_t index ) { vector[index] = a; };
foreachDevice( size, assign );
}
else
{
auto assign = [=] __host__ ( size_t index ) { vector[index] = a; };
foreachHost( size, assign );
}
}

但是,这引入了代码重复,实际上使使用 lambda 的整个想法对这个例子毫无用处。

有没有办法完成我想做的事情,或者这是实验性功能中的错误?实际上,定义一个 __host__ __device__ programming guide 的第一个示例中明确提到了 lambda。 .即使对于那个更简单的示例(只需从 lambda 返回一个常量值)我也找不到在主机和设备上使用 lambda 表达式的方法。

这是完整的代码,使用选项编译 -std=c++11 --expt-extended-lambda :

#include <iostream>
using namespace std;

template<typename Operation> void foreachHost( size_t size, Operation o )
{
for( size_t i = 0; i < size; ++i )
{
o( i );
}
}

template<typename Operation> __global__ void kernel_foreach( Operation o )
{
size_t index = blockIdx.x * blockDim.x + threadIdx.x;
o( index );
}

template<typename Operation> void foreachDevice( size_t size, Operation o )
{
size_t blocksize = 32;
size_t gridsize = size/32;
kernel_foreach<<<gridsize,blocksize>>>( o );
}

__global__ void printFirstElementOnDevice( double* vector )
{
printf( "dVector[0] = %f\n", vector[0] );
}

void assignScalarHost( size_t size, double* vector, double a )
{
auto assign = [=] ( size_t index ) { vector[index] = a; };
foreachHost( size, assign );
}

void assignScalarDevice( size_t size, double* vector, double a )
{
auto assign = [=] __device__ ( size_t index ) { vector[index] = a; };
foreachDevice( size, assign );
}

// compile error:
template<bool onDevice> void assignScalar( size_t size, double* vector, double a )
{
auto assign = [=] __host__ __device__ ( size_t index ) { vector[index] = a; };
if( onDevice )
{
foreachDevice( size, assign );
}
else
{
foreachHost( size, assign );
}
}

// works:
template<bool onDevice> void assignScalar2( size_t size, double* vector, double a )
{
if( onDevice )
{
auto assign = [=] __device__ ( size_t index ) { vector[index] = a; };
foreachDevice( size, assign );
}
else
{
auto assign = [=] __host__ ( size_t index ) { vector[index] = a; };
foreachHost( size, assign );
}
}

int main()
{
size_t SIZE = 32;

double* hVector = new double[SIZE];
double* dVector;
cudaMalloc( &dVector, SIZE*sizeof(double) );

// clear memory
for( size_t i = 0; i < SIZE; ++i )
{
hVector[i] = 0;
}
cudaMemcpy( dVector, hVector, SIZE*sizeof(double), cudaMemcpyHostToDevice );

assignScalarHost( SIZE, hVector, 1.0 );
cout << "hVector[0] = " << hVector[0] << endl;

assignScalarDevice( SIZE, dVector, 2.0 );
printFirstElementOnDevice<<<1,1>>>( dVector );
cudaDeviceSynchronize();

assignScalar2<false>( SIZE, hVector, 3.0 );
cout << "hVector[0] = " << hVector[0] << endl;

assignScalar2<true>( SIZE, dVector, 4.0 );
printFirstElementOnDevice<<<1,1>>>( dVector );
cudaDeviceSynchronize();

// assignScalar<false>( SIZE, hVector, 5.0 );
// cout << "hVector[0] = " << hVector[0] << endl;
//
// assignScalar<true>( SIZE, dVector, 6.0 );
// printFirstElementOnDevice<<<1,1>>>( dVector );
// cudaDeviceSynchronize();

cudaError_t error = cudaGetLastError();
if(error!=cudaSuccess)
{
cout << "ERROR: " << cudaGetErrorString(error);
}
}

我使用的是 CUDA 7.5 的生产版本。

更新

我为 assignScalar 函数尝试了第三个版本:

template<bool onDevice> void assignScalar3( size_t size, double* vector, double a )
{
#ifdef __CUDA_ARCH__
#define LAMBDA_HOST_DEVICE __device__
#else
#define LAMBDA_HOST_DEVICE __host__
#endif

auto assign = [=] LAMBDA_HOST_DEVICE ( size_t index ) { vector[index] = a; };
if( onDevice )
{
foreachDevice( size, assign );
}
else
{
foreachHost( size, assign );
}
}

它编译运行没有错误,但是设备版本(assignScalar3<true>)没有被执行。实际上,我认为__CUDA_ARCH__将始终是未定义的(因为该函数不是 __device__ )但我明确检查了它在定义的地方有一个编译路径。

最佳答案

我试图通过问题中提供的示例完成的任务在 CUDA 7.5 中是不可能的,尽管它没有明确排除在实验性 lambda 支持的允许情况之外。

根据博文 CUDA 8 Features Revealed,NVIDIA 宣布 CUDA Toolkit 8.0 将支持 __host__ __device__ lambda 作为一项实验性功能.

我已验证我的示例适用于 CUDA 8 Release Candidate(Cuda 编译工具,版本 8.0,V8.0.26)。

这是我最终使用的代码,使用 nvcc -std=c++11 --expt-extended-lambda 编译:

#include <iostream>
using namespace std;

template<typename Operation> __global__ void kernel_foreach( Operation o )
{
size_t i = blockIdx.x * blockDim.x + threadIdx.x;
o( i );
}

template<bool onDevice, typename Operation> void foreach( size_t size, Operation o )
{
if( onDevice )
{
size_t blocksize = 32;
size_t gridsize = size/32;
kernel_foreach<<<gridsize,blocksize>>>( o );
}
else
{
for( size_t i = 0; i < size; ++i )
{
o( i );
}
}
}

__global__ void printFirstElementOnDevice( double* vector )
{
printf( "dVector[0] = %f\n", vector[0] );
}

template<bool onDevice> void assignScalar( size_t size, double* vector, double a )
{
auto assign = [=] __host__ __device__ ( size_t i ) { vector[i] = a; };
foreach<onDevice>( size, assign );
}

int main()
{
size_t SIZE = 32;

double* hVector = new double[SIZE];
double* dVector;
cudaMalloc( &dVector, SIZE*sizeof(double) );

// clear memory
for( size_t i = 0; i < SIZE; ++i )
{
hVector[i] = 0;
}
cudaMemcpy( dVector, hVector, SIZE*sizeof(double), cudaMemcpyHostToDevice );

assignScalar<false>( SIZE, hVector, 3.0 );
cout << "hVector[0] = " << hVector[0] << endl;

assignScalar<true>( SIZE, dVector, 4.0 );
printFirstElementOnDevice<<<1,1>>>( dVector );
cudaDeviceSynchronize();

cudaError_t error = cudaGetLastError();
if(error!=cudaSuccess)
{
cout << "ERROR: " << cudaGetErrorString(error);
}
}

关于c++ - CUDA 7.5 实验性 __host__ __device__ lambda,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32567479/

36 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com