我正在使用我们组织内部开发的大型CUDA矩阵库。我需要保存CUDA RNG的状态,以便对长时间运行的模拟进行快照,并能够在以后恢复它。这很简单,例如,python + numpy:
state = numpy.random.get_state()
# state is a tuple with 5 fields which can be pickled, etc.
...
numpy.random.set_state(state)
我似乎无法在CUDA主机api中找到相同的功能。您可以设置种子和偏移量,但无法检索它以进行保存。设备API似乎提供了这样的东西,但是这个库使用了主机api,改变它会很奇怪。
我正在考虑的黑客解决方案是跟踪RNG的呼叫数量(设置种子时重置),并简单地重复调用RNG功能。但是,我不确定函数参数是否必须相同,例如矩阵形状等,使其达到相同的状态。类似地,如果调用的数量等于用于初始化RNG的偏移参数,这也可以工作,即,如果我将RNG调用200次,我可以将偏移设置为200.但是,在python中,偏移量为每次调用时状态可以增加1以上,所以这也可能是错误的。
对如何解决这个问题的任何见解表示赞赏!
答案 0 :(得分:1)
对于CURAND Host API,我相信curandSetGeneratorOffset()可能适用于此。
以下是curand主机API文档中的修改示例:
$ cat t721.cu
/*
* This program uses the host CURAND API to generate 10
* pseudorandom floats. And then regenerate those same floats.
*/
#include <stdio.h>
#include <stdlib.h>
#include <cuda.h>
#include <curand.h>
#define CUDA_CALL(x) do { if((x)!=cudaSuccess) { \
printf("Error at %s:%d\n",__FILE__,__LINE__);\
return EXIT_FAILURE;}} while(0)
#define CURAND_CALL(x) do { if((x)!=CURAND_STATUS_SUCCESS) { \
printf("Error at %s:%d\n",__FILE__,__LINE__);\
return EXIT_FAILURE;}} while(0)
int main(int argc, char *argv[])
{
size_t n = 10;
size_t i;
curandGenerator_t gen;
float *devData, *hostData;
/* Allocate n floats on host */
hostData = (float *)calloc(n, sizeof(float));
/* Allocate n floats on device */
CUDA_CALL(cudaMalloc((void **)&devData, n*sizeof(float)));
/* Create pseudo-random number generator */
CURAND_CALL(curandCreateGenerator(&gen,
CURAND_RNG_PSEUDO_DEFAULT));
/* Set seed */
CURAND_CALL(curandSetPseudoRandomGeneratorSeed(gen,
1234ULL));
// generator offset = 0
/* Generate n floats on device */
CURAND_CALL(curandGenerateUniform(gen, devData, n));
// generator offset = n
/* Generate n floats on device */
CURAND_CALL(curandGenerateUniform(gen, devData, n));
// generator offset = 2n
/* Copy device memory to host */
CUDA_CALL(cudaMemcpy(hostData, devData, n * sizeof(float),
cudaMemcpyDeviceToHost));
/* Show result */
for(i = 0; i < n; i++) {
printf("%1.4f ", hostData[i]);
}
printf("\n\n");
CURAND_CALL(curandSetGeneratorOffset(gen, n));
// generator offset = n
CURAND_CALL(curandGenerateUniform(gen, devData, n));
// generator offset = 2n
/* Copy device memory to host */
CUDA_CALL(cudaMemcpy(hostData, devData, n * sizeof(float),
cudaMemcpyDeviceToHost));
/* Show result */
for(i = 0; i < n; i++) {
printf("%1.4f ", hostData[i]);
}
printf("\n");
/* Cleanup */
CURAND_CALL(curandDestroyGenerator(gen));
CUDA_CALL(cudaFree(devData));
free(hostData);
return EXIT_SUCCESS;
}
$ nvcc -o t721 t721.cu -lcurand
$ ./t721
0.7816 0.2338 0.6791 0.2824 0.6299 0.1212 0.4333 0.3831 0.5136 0.2987
0.7816 0.2338 0.6791 0.2824 0.6299 0.1212 0.4333 0.3831 0.5136 0.2987
$
因此,您需要跟踪生成的随机数的数量(不 RNG函数调用的数量),直到您执行检查点为止,并保存它。
重新启动时,以相同的方式初始化生成器:
/* Create pseudo-random number generator */
CURAND_CALL(curandCreateGenerator(&gen,
CURAND_RNG_PSEUDO_DEFAULT));
/* Set seed */
CURAND_CALL(curandSetPseudoRandomGeneratorSeed(gen,
1234ULL));
然后按先前生成的值(n
)的数量前进:
CURAND_CALL(curandSetGeneratorOffset(gen, n));
答案 1 :(得分:0)
因此,可以通过跟踪使用curandSetGeneratorOffset生成的32位值的数量来存储和恢复状态。该算法类似于:
template<typename T> RNG(T* X, size_T N /*number of values*/){
...
if (sizeof(T) == 1)
offset += (N+4-1)/4;
else if (sizeof(T) == 2)
offset += (N+2-1)/4;
else if (sizeof(T) == 4 || USING_GENERATE_UNIFORM_DOUBLE)
offset += N;
else if (sizeof(T) == 8)
offset += 2*N;
}
对于8位值,对于生成的N值,将偏移量提前N *下一个最高倍数4。对于16,通过N *前进到下一个倍数2.对于32提前N,并且提前64提前2 * N.
但是,如果你使用GenerateUniformDouble,你只需要提前N,而不是2 * N.我不确定为什么。
感谢您的帮助!