저는 cuRAND 라이브러리를 사용하여 생성기 및 상태의 개념을 이해했습니다. 용어 state
은 아직 나를 혼란스럽게 만들었으므로 일부 코드의 도움으로 내 이해를 명확히하고 싶습니다.CUDA : PRNG 생성기 및 상태
int main() {
float *hData, *dData;
curandState *devState;
size_t nThreads = 4;
size_t nRows = 10;
size_t n = nRows * nThreads;
hData = new float[n];
cudaMalloc((void**)&dData, n * sizeof(float));
cudaMemset(dData, 0, n * sizeof(float));
cudaMalloc((void**)&devState, nThreads * sizeof(curandState));
initCurand <<< 1, nThreads >>> (devState, 1234);
cudaDeviceSynchronize();
testrand <<< 1, nThreads >>> (devState, dData, nRows);
cudaDeviceSynchronize();
cudaMemcpy(hData, dData, n*sizeof(int), cudaMemcpyDeviceToHost);
cudaFree(devState);
cudaFree(dData);
free(hData);
}
다음은 커널입니다.
__global__ void initCurand(curandState *state, unsigned long seed)
{
curand_init(seed, threadIdx.x, 0, &state[threadIdx.x]);
}
__global__ void testrand(curandState *state, float *d1, int rows)
{
int idx = threadIdx.x;
int stride = blockDim.x;
for (int i = 0; i < rows; i++)
{
d1[idx + i * stride] = curand_uniform(&state[idx]);
}
}
간단히 말해, 스레드 수만큼 상태가 있습니다. 각 쓰레드는 각각의 상태에서 10 개의 난수를 소비합니다. 출력은 다음과 같습니다.
0.145468 0.820181 0.550399 0.294830
0.434899 0.926417 0.811845 0.308556
0.870710 0.511765 0.782640 0.620706
0.455165 0.537594 0.742539 0.535606
0.857093 0.809246 0.541354 0.497212
0.582418 0.017524 0.195556 0.898062
0.201404 0.449338 0.006050 0.041652
0.786745 0.799349 0.093755 0.994597
0.300772 0.136307 0.648018 0.970036
0.366787 0.377424 0.096621 0.495483
따라서 상태는 실제로 자체 생성기입니다. 각 생성자/상태는 균일 한 난수 분포를 생성하고 다른 상태는 생성 된 수가 반복되지 않도록 거리를 유지합니다. ==> Q1
cuRAND 가이드에서 많은 상태를 생성하는 것이 계산 집약적이라는 것을 알게되었습니다. 또한 그것을 실험하기 위해 실험했습니다. 어떻게 하나의 state
(변수)를 사용하여 각 스레드가 주어진 (단일) 분포에서 다른 난수 (또는 다음 난수가 대기열)를 소비하도록 난수를 생성 할 수 있습니다. ==> Q2
예 : I 네 개의 쓰레드가 다음과 같이 하나의 상태 변수가되는 난수 (가상) 분포를 생성
2-6 100 26 81 72 78 21 33 57 19 32 ...
------ - 1st Cycle ------ 2nd Cycle ------
Thread_1 : - 2 --------- 81 -
Thread_2 : - 6 -------
Thread_3 - - - 100 72 ------ 78은 -
Thread_4 - 26 -------- 21 -
이 PO는 단일 상태 변수를 사용하는 것이 가능한가?
따라서 상태 수는 항상 이러한 난수를 소비 할 커널에 대해 시작하는 스레드와 동일해야합니다. 따라서 각 스레드가 자체 배포 (균일, 정상 등)를 수행한다고 말할 수 있습니다. 응용 프로그램에서 난수 생성 스레드를 소비하려는 커널과 별도로 유지해야한다고 말할 수 있습니까? 지금까지 위의 예에서도'testrand'를'curand()'에서 생성 된 숫자를 사용하는 커널로 가져 왔습니다. – Psypher
일반적인 사용 사례 중 하나는 RNG를 즉석에서 수행하는 것입니다. 생성 된 난수는 생성 된 난수에 의해 소비됩니다. 아무것도 분리 할 필요가 없습니다. 만약 당신이 선행 세대 (아마도 별도의 커널에 의해) 그렇다면, 당신은 그들을 소비하기 전에 숫자가 생성되었는지 확인해야합니다. –