[英]CUDA C++11, array of lambdas, function by index, not working
我在嘗試使CUDA程序通過其索引管理一系列lambda時遇到麻煩。 重現問題的示例代碼
#include <cuda.h>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <cassert>
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, const char *file, int line, bool abort=true){
if (code != cudaSuccess) {
fprintf(stderr,"GPUassert: %s %s %d\n",
cudaGetErrorString(code), file, line);
if (abort) exit(code);
}
}
template<typename Lambda>
__global__ void kernel(Lambda f){
int t = blockIdx.x * blockDim.x + threadIdx.x;
printf("device: thread %i: ", t);
printf("f() = %i\n", f() );
}
int main(int argc, char **argv){
// arguments
if(argc != 2){
fprintf(stderr, "run as ./prog i\nwhere 'i' is function index");
exit(EXIT_FAILURE);
}
int i = atoi(argv[1]);
// lambdas
auto lam0 = [] __host__ __device__ (){ return 333; };
auto lam1 = [] __host__ __device__ (){ return 777; };
// make vector of functions
std::vector<int(*)()> v;
v.push_back(lam0);
v.push_back(lam1);
// host: calling a function by index
printf("host: f() = %i\n", (*v[i])() );
// device: calling a function by index
kernel<<< 1, 1 >>>( v[i] ); // does not work
//kernel<<< 1, 1 >>>( lam0 ); // does work
gpuErrchk( cudaPeekAtLastError() );
gpuErrchk( cudaDeviceSynchronize() );
return EXIT_SUCCESS;
}
編譯
nvcc -arch sm_60 -std=c++11 --expt-extended-lambda main.cu -o prog
我在運行時遇到的錯誤是
➜ cuda-lambda ./prog 0
host: f() = 333
device: GPUassert: invalid program counter main.cu 53
似乎CUDA無法管理int(*)()函數指針形式(盡管主機c ++可以正常工作)。 另一方面,每個lambda都以不同的數據類型進行管理,無論它們在代碼上是否相同且具有相同的約定。 那么,如何在CUDA中實現索引功能呢?
這里有一些注意事項。
盡管您建議要“管理lambda數組”,但實際上您依賴於lambda到函數指針的優美轉換(當lambda不捕獲時可能)。
當您將某物標記為__host__ __device__
,您在向編譯器聲明需要編譯所述項目的兩個副本(兩個明顯不同的入口點):一個用於CPU,一個用於GPU。
當我們使用__host__ __device__
lambda並要求它降級為函數指針時,我們將面臨“選擇哪個函數指針(入口點)?”的問題。 編譯器不再具有攜帶實驗性lambda對象的選項,因此必須為您的向量選擇一個或另一個(主機或設備,CPU或GPU)。 無論選擇哪種載體,如果在錯誤的環境中使用,載體都可能(會)斷裂。
從這樣的一個外賣的是,你的兩個測試用例是不一樣的。 在一種情況下(斷開),您正在向內核傳遞函數指針(因此,將內核模板化為接受函數指針參數),在另一種情況下(有效),您正在將lambda傳遞給內核(因此,對內核進行了模板化接受lambda參數)。
在我看來,這里的問題不僅是由於使用容器引起的,而是由於您使用的容器類型引起的。 我可以通過將向量轉換為實際lambda類型的向量的簡單方式(見下文)進行演示。 在那種情況下,我們可以使代碼“工作”(之類),但是由於每個lambda都有唯一的類型 ,因此這是無趣的演示。 我們可以創建一個多元素向量,但是我們可以存儲在其中的唯一元素是您的兩個lambda之一(不能同時使用)。
如果我們使用可以處理不同類型的容器(例如std::tuple
),也許我們可以在這里取得一些進展,但是我知道沒有直接方法可以對這種容器的元素進行索引。 即使可以,對於每個lambda,都必須實例化接受lambda作為參數/模板類型的模板內核。
在我看來,函數指針避免了這種特殊類型的“麻煩”。
因此,作為對此問題的解答:
那么,如何在CUDA中實現索引功能呢?
我建議暫時將主機代碼中按索引的功能與設備代碼中按索引的功能分開(例如,兩個單獨的容器),對於設備代碼中按索引的功能,則可以使用任何一種技術(使用或依賴lambda表達式)涵蓋的其他問題,比如這一個 。
這是一個工作的示例(我認為)證明了上面的注釋,我們可以創建lambda“ type”的向量,並使用該向量的結果元素作為宿主和設備代碼中的lambda:
$ cat t64.cu
#include <cuda.h>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <cassert>
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, const char *file, int line, bool abort=true){
if (code != cudaSuccess) {
fprintf(stderr,"GPUassert: %s %s %d\n",
cudaGetErrorString(code), file, line);
if (abort) exit(code);
}
}
template<typename Lambda>
__global__ void kernel(Lambda f){
int t = blockIdx.x * blockDim.x + threadIdx.x;
printf("device: thread %i: ", t);
printf("f() = %i\n", f() );
}
template <typename T>
std::vector<T> fill(T L0, T L1){
std::vector<T> v;
v.push_back(L0);
v.push_back(L1);
return v;
}
int main(int argc, char **argv){
// arguments
if(argc != 2){
fprintf(stderr, "run as ./prog i\nwhere 'i' is function index");
exit(EXIT_FAILURE);
}
int i = atoi(argv[1]);
// lambdas
auto lam0 = [] __host__ __device__ (){ return 333; };
auto lam1 = [] __host__ __device__ (){ return 777; };
auto v = fill(lam0, lam0);
// make vector of functions
// std::vector< int(*)()> v;
// v.push_back(lam0);
// v.push_back(lam1);
// host: calling a function by index
// host: calling a function by index
printf("host: f() = %i\n", (*v[i])() );
// device: calling a function by index
kernel<<< 1, 1 >>>( v[i] ); // does not work
//kernel<<< 1, 1 >>>( lam0 ); // does work
gpuErrchk( cudaPeekAtLastError() );
gpuErrchk( cudaDeviceSynchronize() );
return EXIT_SUCCESS;
}
$ nvcc -arch sm_61 -std=c++11 --expt-extended-lambda t64.cu -o t64
$ cuda-memcheck ./t64 0
========= CUDA-MEMCHECK
host: f() = 333
device: thread 0: f() = 333
========= ERROR SUMMARY: 0 errors
$ cuda-memcheck ./t64 1
========= CUDA-MEMCHECK
host: f() = 333
device: thread 0: f() = 333
========= ERROR SUMMARY: 0 errors
$
如上所述,此代碼不是明智的代碼。 它是先進的,可以證明這一點。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.