私は推力の初心者です。スラストのプレゼンテーションとサンプルはすべてホストコードのみを表示しています。ユーザーの書かれたカーネル内でのスラスト
自分のカーネルにdevice_vectorを渡すことができるかどうか知りたいですか?どうやって? "はい"の場合、カーネル/デバイスコードの内部で許可されている操作は何ですか?
私は推力の初心者です。スラストのプレゼンテーションとサンプルはすべてホストコードのみを表示しています。ユーザーの書かれたカーネル内でのスラスト
自分のカーネルにdevice_vectorを渡すことができるかどうか知りたいですか?どうやって? "はい"の場合、カーネル/デバイスコードの内部で許可されている操作は何ですか?
推力によって割り当て/処理されたデータを使用する場合は、割り当てられたデータの生ポインタを取得するだけです。
int * raw_ptr = thrust::raw_pointer_cast(dev_ptr);
あなたは、私が試したことがないカーネルで推力ベクトルを割り当てたいが、私は を動作するとは思わないし、それが動作する場合にも、私はそれがすべての利益を提供するとは思わない場合。
この質問に対する更新された回答を提供したいと思います。
推力1.8から、CUDA Thrustプリミティブをthrust::seq
実行ポリシーと組み合わせて、単一のCUDAスレッド内(または単一のCPUスレッド内で順次)で実行することができます。以下に例を示します。
スレッド内で並列実行を行う場合は、CUBを使用することを検討してください。このルーチンは、カードが動的並列処理を可能にする場合にはスレッドブロック内から呼び出すことができます。ここで
は、これは私の前の回答に更新されスラスト
#include <stdio.h>
#include <thrust/reduce.h>
#include <thrust/execution_policy.h>
/********************/
/* CUDA ERROR CHECK */
/********************/
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, 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);
}
}
__global__ void test(float *d_A, int N) {
float sum = thrust::reduce(thrust::seq, d_A, d_A + N);
printf("Device side result = %f\n", sum);
}
int main() {
const int N = 16;
float *h_A = (float*)malloc(N * sizeof(float));
float sum = 0.f;
for (int i=0; i<N; i++) {
h_A[i] = i;
sum = sum + h_A[i];
}
printf("Host side result = %f\n", sum);
float *d_A; gpuErrchk(cudaMalloc((void**)&d_A, N * sizeof(float)));
gpuErrchk(cudaMemcpy(d_A, h_A, N * sizeof(float), cudaMemcpyHostToDevice));
test<<<1,1>>>(d_A, N);
}
との一例です。スラスト1.8.1最低
、CUDAスラストプリミティブは、CUDA 動的並列を利用する単一CUDAスレッド内で並列に実行するthrust::device
実行ポリシーと組み合わせることができます。以下に例を示します。
#include <stdio.h>
#include <thrust/reduce.h>
#include <thrust/execution_policy.h>
#include "TimingGPU.cuh"
#include "Utilities.cuh"
#define BLOCKSIZE_1D 256
#define BLOCKSIZE_2D_X 32
#define BLOCKSIZE_2D_Y 32
/*************************/
/* TEST KERNEL FUNCTIONS */
/*************************/
__global__ void test1(const float * __restrict__ d_data, float * __restrict__ d_results, const int Nrows, const int Ncols) {
const unsigned int tid = threadIdx.x + blockDim.x * blockIdx.x;
if (tid < Nrows) d_results[tid] = thrust::reduce(thrust::seq, d_data + tid * Ncols, d_data + (tid + 1) * Ncols);
}
__global__ void test2(const float * __restrict__ d_data, float * __restrict__ d_results, const int Nrows, const int Ncols) {
const unsigned int tid = threadIdx.x + blockDim.x * blockIdx.x;
if (tid < Nrows) d_results[tid] = thrust::reduce(thrust::device, d_data + tid * Ncols, d_data + (tid + 1) * Ncols);
}
/********/
/* MAIN */
/********/
int main() {
const int Nrows = 64;
const int Ncols = 2048;
gpuErrchk(cudaFree(0));
// size_t DevQueue;
// gpuErrchk(cudaDeviceGetLimit(&DevQueue, cudaLimitDevRuntimePendingLaunchCount));
// DevQueue *= 128;
// gpuErrchk(cudaDeviceSetLimit(cudaLimitDevRuntimePendingLaunchCount, DevQueue));
float *h_data = (float *)malloc(Nrows * Ncols * sizeof(float));
float *h_results = (float *)malloc(Nrows * sizeof(float));
float *h_results1 = (float *)malloc(Nrows * sizeof(float));
float *h_results2 = (float *)malloc(Nrows * sizeof(float));
float sum = 0.f;
for (int i=0; i<Nrows; i++) {
h_results[i] = 0.f;
for (int j=0; j<Ncols; j++) {
h_data[i*Ncols+j] = i;
h_results[i] = h_results[i] + h_data[i*Ncols+j];
}
}
TimingGPU timerGPU;
float *d_data; gpuErrchk(cudaMalloc((void**)&d_data, Nrows * Ncols * sizeof(float)));
float *d_results1; gpuErrchk(cudaMalloc((void**)&d_results1, Nrows * sizeof(float)));
float *d_results2; gpuErrchk(cudaMalloc((void**)&d_results2, Nrows * sizeof(float)));
gpuErrchk(cudaMemcpy(d_data, h_data, Nrows * Ncols * sizeof(float), cudaMemcpyHostToDevice));
timerGPU.StartCounter();
test1<<<iDivUp(Nrows, BLOCKSIZE_1D), BLOCKSIZE_1D>>>(d_data, d_results1, Nrows, Ncols);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
printf("Timing approach nr. 1 = %f\n", timerGPU.GetCounter());
gpuErrchk(cudaMemcpy(h_results1, d_results1, Nrows * sizeof(float), cudaMemcpyDeviceToHost));
for (int i=0; i<Nrows; i++) {
if (h_results1[i] != h_results[i]) {
printf("Approach nr. 1; Error at i = %i; h_results1 = %f; h_results = %f", i, h_results1[i], h_results[i]);
return 0;
}
}
timerGPU.StartCounter();
test2<<<iDivUp(Nrows, BLOCKSIZE_1D), BLOCKSIZE_1D>>>(d_data, d_results1, Nrows, Ncols);
gpuErrchk(cudaPeekAtLastError());
gpuErrchk(cudaDeviceSynchronize());
printf("Timing approach nr. 2 = %f\n", timerGPU.GetCounter());
gpuErrchk(cudaMemcpy(h_results1, d_results1, Nrows * sizeof(float), cudaMemcpyDeviceToHost));
for (int i=0; i<Nrows; i++) {
if (h_results1[i] != h_results[i]) {
printf("Approach nr. 2; Error at i = %i; h_results1 = %f; h_results = %f", i, h_results1[i], h_results[i]);
return 0;
}
}
printf("Test passed!\n");
}
上記の例ではReduce matrix rows with CUDAと同じ意味で、行列の行の削減を行うが、それは、ユーザ記述カーネルから直接CUDAスラストプリミティブを呼び出すことによって、すなわち、上記ポストは異なる行われます。また、上記の例では、同じ操作を2つの実行ポリシー、つまりthrust::seq
とthrust::device
で実行した場合のパフォーマンスを比較しています。以下に、パフォーマンスの違いを示すグラフを示します。
パフォーマンスはケプラーK20c上とマクスウェルのGeForce GTX 850Mで評価されています。
FabrizioM:私はカーネルにdevice_vectorを渡して、カーネルの内部でsize()を呼び出すことができたらいいと思っていました。これは現時点では不可能なようです。私はraw_pointer_castを使用し、カーネルに別のパラメータとしてサイズを送信します。 –
アシュウィン:そうです。あなたがしようとしていることは不可能です。別々にサイズを渡す必要があります。 –