首页 > 编程语言 >CUDA 的随机数算法 API

CUDA 的随机数算法 API

时间:2023-05-06 09:47:02浏览次数:54  
标签:__ curand init state API CUDA 随机数 seed

参考自 Nvidia cuRand 官方 API 文档

一、具体使用场景

如下是是在 dropout 优化中手写的 uniform_random 的 Kernel:

#include <cuda_runtime.h>
#include <curand_kernel.h>

__device__ inline float cinn_nvgpu_uniform_random_fp32(int seed){
  curandStatePhilox4_32_10_t state;
  int idx = threadIdx.x + blockIdx.x * blockDim.x;
  curand_init(seed, idx, 1, &state);
  return curand_uniform(&state);
}

二、API 解析

我们首先来看 curand_init 函数的签名和语义:

__device__ void
curand_init(unsigned long long seed,
            unsigned long long subsequence,
            unsigned long long offset,
            curandStatePhilox4_32_10_t *state)

给定相同的seed、sequence、offset 参数下,curand_init 会保证产生相同的其实状态 state。另外此函数会在调用 2^67 ⋅ sequence + offset次 cu_rand API 之后「重置」为起始状态。关于 sequence 和 offset 的如何生效的机制,参考 StackOverflow

注意:在 seed 相同、sequence 不同时,一般不会产生有统计学上关联的结果。原文:Sequences generated with the same seed and different sequence numbers will not have statistically correlated values.

另外在CUDA 并行产生随机数实践上,也有一些经验上的建议:

  • 若保证高质量的伪随机数,建议使用不同的 seed
  • 若是并行在一个「实验」里,建议指定不同的sequence参数,且最好「单调递增」
  • 若果线程里的config都是一样,即 state 一样,则可以把「随机状态量」放到 global memory里,以减少 setup 开销

参考原文:For the highest quality parallel pseudorandom number generation, each experiment should be assigned a unique seed. Within an experiment, each thread of computation should be assigned a unique sequence number. If an experiment spans multiple kernel launches, it is recommended that threads between kernel launches be given the same seed, and sequence numbers be assigned in a monotonically increasing way. If the same configuration of threads is launched, random state can be preserved in global memory between launches to avoid state setup time.

然后我们看下Nvidia 主要提供了哪些常用的随机数生成API:

__device__ float
curand_uniform (curandState_t *state);   // <----  It may return from 0.0 to 1.0, where 1.0 is included and 0.0 is excluded.

__device__ float
curand_normal (curandState_t *state);    // <-----  returns a single normally distributed float with mean 0.0 and standard deviation 1.0.

__device__ float
curand_log_normal (curandState_t *state, float mean, float stddev); // <----- returns a single log-normally distributed float based on a normal distribution with the given mean and standard deviation.

// 如下是上述 3 个API 的 double 版本
__device__ double
curand_uniform_double (curandState_t *state);

__device__ double
curand_normal_double (curandState_t *state);

__device__ double
curand_log_normal_double (curandState_t *state, double mean, double stddev);

上面的 device API 在每次调用时,只会生成一个 float/double 的随机数。Nvidia 同样提供了一次可以生成 2个或4个 device API:

__device__ uint4
curand4 (curandStatePhilox4_32_10_t *state);

__device__ float4
curand_uniform4 (curandStatePhilox4_32_10_t *state);

__device__ float4
curand_normal4 (curandStatePhilox4_32_10_t *state);

__device__ float4
curand_log_normal4 (curandStatePhilox4_32_10_t *state, float mean, float stddev);

从上面的函数接口以及 Nvidia 的文档来看,在初始化某种类型的 state 状态量后,每次调用类似 curand() 的 API 后,state 都会自动进行 offset 偏移。

因此,Nvidia 官网上也提供了单独对 state 进行位移的 API,其效果等价于调用多次无返回值的 curand() API,且性能更好:

__device__ void
skipahead(unsigned long long n, curandState_t *state); // <----- == calls n*curand()

__device__ void
skipahead_sequence(unsigned long long n, curandState_t *state); // <----- == calls n*2^67 curand()

三、性能分析

Nvidia 的官网明确指出了存在的性能问题,给开发者实现高性能 Kernel 提供了充分的经验指导:

  • curand_init()要比curand()和curand_uniform()慢!
  • curand_init()在 offset 比较大时性能也会比小 offset 差!
  • save/load操作 state 比每次重复创建起始 state 性能要快很多 !

原文如下:Calls to curand_init() are slower than calls to curand() or curand_uniform(). Large offsets to curand_init() take more time than smaller offsets. It is much faster to save and restore random generator state than to recalculate the starting state repeatedly.

对于上述第三点,Nvidia 建议可以将 state 存放到 global memory 中,如下是一个样例代码:

__global__ void example(curandState *global_state)
{
    curandState local_state;
    local_state = global_state[threadIdx.x];
    for(int i = 0; i < 10000; i++) {
        unsigned int x = curand(&local_state);
        ...
    }
    global_state[threadIdx.x] = local_state;
}

从另一个维度来讲,相对于A产生随机数操作的API,初始化 state 会占用更多的「寄存器」和 local memory 资源。因此 nvidia 建议将 curand_initcurand() API 拆分放到不同的 Kernel 中,可以获得最大的性能收益;

原文:Initialization of the random generator state generally requires more registers and local memory than random number generation. It may be beneficial to separate calls to curand_init() and curand() into separate kernels for maximum performance.
State setup can be an expensive operation. One way to speed up the setup is to use different seeds for each thread and a constant sequence number of 0. This can be especially helpful if many generators need to be created. While faster to set up, this method provides less guarantees about the mathematical properties of the generated sequences. If there happens to be a bad interaction between the hash function that initializes the generator state from the seed and the periodicity of the generators, there might be threads with highly correlated outputs for some seed values. We do not know of any problem values; if they do exist they are likely to be rare.

Nvidia 提供的 API 样例代码如下:

#include <stdio.h>
#include <stdlib.h>

#include <cuda_runtime.h>
#include <curand_kernel.h>

#define CUDA_CALL(x) do { if((x) != cudaSuccess) { \
    printf("Error at %s:%d\n",__FILE__,__LINE__); \
    return EXIT_FAILURE;}} while(0)

__global__ void setup_kernel(curandState *state)
{
    int id = threadIdx.x + blockIdx.x * blockDim.x;
    /* Each thread gets same seed, a different sequence
       number, no offset */
    curand_init(1234, id, 0, &state[id]);
}

__global__ void generate_uniform_kernel(curandStatePhilox4_32_10_t *state,
                                int n,
                                unsigned int *result)
{
    int id = threadIdx.x + blockIdx.x * blockDim.x;
    unsigned int count = 0;
    float x;
    /* Copy state to local memory for efficiency */
    curandStatePhilox4_32_10_t localState = state[id];
    /* Generate pseudo-random uniforms */
    for(int i = 0; i < n; i++) {
        x = curand_uniform(&localState);
        /* Check if > .5 */
        if(x > .5) {
            count++;
        }
    }
    /* Copy state back to global memory */
    state[id] = localState;
    /* Store results */
    result[id] += count;
}

标签:__,curand,init,state,API,CUDA,随机数,seed
From: https://www.cnblogs.com/CocoML/p/17376020.html

相关文章

  • salt-api
    添加用户useradd-M-s/sbin/nologinsaltapipasswdsaltapi新增配置文件#cat/etc/salt/master.d/eauth.confexternal_auth:pam:saltapi:#用户-.*#该配置文件给予saltapi用户所有模块使用权限,出于安全考虑一般只给予特定模块使用权限......
  • go测试库之apitest
    前言使用go语言做开发差不多快一年了,主要用来写后端Web服务,从一开始吐槽他的结构体,比如创建个复杂的JSON格式数据,那是相当的痛苦。还有err处理写的巨麻烦。当然,go也有爽的地方,创建个线协程简直太简单了。到后来慢慢接受,觉得效率还行,因为是静态强类型语言,在修改完项目代码之......
  • C++11生成随机数
    一、random_device类classrandom_device{public:typedefunsignedintresult_type;//constructor构造函数explicitrandom_device(conststd::string&token="");//propertiesstaticresult_typemin();staticresult_typemax()......
  • CUDA入门笔记
    一个SM(StreamingMultiprocessor)中的所有SP(StreamingProcessor)是分成Warp的,共享同一个Memory和InstructionUnit(指令单元)。从硬件角度讲,一个GPU由多个SM组成(当然还有其他部分),一个SM包含有多个SP(以及还有寄存器资源,SharedMemory资源,L1cache,Scheduler,SPU,LD/ST单元等等)SM采......
  • 武装你的WEBAPI-OData Versioning
    本文属于OData系列目录武装你的WEBAPI-OData入门武装你的WEBAPI-OData便捷查询武装你的WEBAPI-OData分页查询武装你的WEBAPI-OData资源更新Delta武装你的WEBAPI-OData之EDM武装你的WEBAPI-OData常见问题武装你的WEBAPI-OData使用Endpoint武装你的WEBAPI-OData聚合查询......
  • RuntimeError: CUDA error: out of memory.
    RuntimeError:CUDAerror:outofmemory.CUDAkernelerrorsmightbeasynchronouslyreportedatsomeotherAPIcall,sothestacktracebelowmightbeincorrect.FordebuggingconsiderpassingCUDA_LAUNCH_BLOCKING=1.这个error的原因是,当期指定的GPU的显存不足,可......
  • windows api编程中 常用变量名pszText 的 psz 代表什么意思
    来自ChatGPT的回答:在WindowsAPI编程中,pszText是一个常见的变量名,通常用于表示一个指向包含文本字符串的缓冲区的指针。其中,psz是一种常见的命名前缀,它代表“指向以零结尾的字符串指针(PointertoZero-terminatedString)”。这是因为在WindowsAPI中,许多函数和结构体成员都需要......
  • MASA MinimalAPI源码解析:为什么我们只写了一个app.MapGet,却生成了三个接口
    源码解析:为什么我们只写了一个app.MapGet,却生成了三个接口1.ServiceBase1.AutoMapRoute源码如下:AutoMapRoute自动创建map路由,MinimalAPI会根据service中的方法,创建对应的api接口。比如上文的一个方法:publicasyncTask<WeatherForecast[]>PostWeather(){re......
  • MASAMinimalAPI:创建MinimalAPI项目
    项目准备1.创建项目,选择webapi。取消勾选使用控制器。创建minimalApi项目2.创建成功后MinimalAPI的接口直接写在program.cs中3.引入nuget包:Masa.Contrib.Service.MinimalAPIsMinimalAPI改造1.在program.cs中加入以下内容将原有的varapp=builder.Build();换成var......
  • Hadoop之HDFS的API操作文件的上传下载参数的优先级
    Hadoop之HDFS的API操作文件的上传下载参数的优先级packagecom.itnihao.hdfs;importorg.apache.hadoop.conf.Configuration;importorg.apache.hadoop.fs.FileSystem;importorg.apache.hadoop.fs.Path;importorg.junit.After;importorg.junit.Before;importorg.jun......