CUDA 7.5 experimental __host__ __device__ lambdas
CUDA 7.5 experimental __host__ __device__ lambdas
我玩了一下实验设备lambdas,它是在CUDA 7.5中引入的,Mark Harris在这篇博文中进行了推广。
对于下面的例子,我删除了很多不需要显示我的问题的东西(我的实际实现看起来更好一点…)。
我试着写一个foreach函数,根据模板参数对设备上的向量(每个元素1个线程)或主机(串行)进行操作。有了这个foreach函数,我可以很容易地实现BLAS函数。作为一个例子,我将一个标量赋值给向量的每个组件(我将完整的代码附加在最后):
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:
lambda的闭包类型("lambda ->void")不能在__global__函数模板实例化的模板实参类型中使用,除非lambda在__device__或__global__函数中定义
如果我从lambda表达式中删除__device__
,我得到同样的错误,如果我删除__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 );
}
}
然而,这引入了代码复制,实际上使使用lambdas的整个想法在本例中毫无用处。
是否有一种方法可以完成我想做的事情,或者这是实验功能中的错误?实际上,在编程指南的第一个示例中明确提到了定义__host__ __device__
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] = %fn", 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支持的允许情况之外。
NVIDIA宣布CUDA Toolkit 8.0将支持__host__ __device__
lambdas作为实验功能,根据博客文章CUDA 8功能揭示。
我验证了我的示例可以与CUDA 8发布候选版本(CUDA编译工具,Release 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] = %fn", 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++20中对lambdas使用"std::bind_front"
- 通过 host() 从 af::array 检索数据会导致错误的数据
- 使用提升 asio 时出现"resolve: Host not found (authoritative)"异常
- mingw32_gt_pch_use_address中的内部错误,在config/i386/host-mingw32.c
- JavaScript箭头函数:我们能否像C ++ lambdas一样捕获值
- cudaMallocManaged for host-initiated variable
- 使用lambdas初始化多维数组
- C++17中没有自动参数的模板lambdas
- 通过引用捕获与移动,lambdas
- 我可以使用匿名lambdas坚持静态值
- lambdas的可继承性是否由标准保证
- C ;使用lambdas在类中有条件地扩展功能(MWE的SEG故障)
- 在具有功能的C++中嵌套无捕获的lambdas?
- Templated Variables Bug With Lambdas in Visual Studio?
- Makefile for host & target
- C 使用多个lambdas/绑定以引用相同的功能
- 直接将lambdas传递到函数
- 使QTConcurrent ::映射与Lambdas一起工作
- C 中的枚举和lambdas的地图
- STD ::仅移动的Lambdas的向量是可能的