参考自 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_init 和 curand() 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;
}

CUDA 的随机数算法 API的更多相关文章

  1. 硬核 - Java 随机数相关 API 的演进与思考(上)

    本系列将 Java 17 之前的随机数 API 以及 Java 17 之后的统一 API 都做了比较详细的说明,并且将随机数的特性以及实现思路也做了一些简单的分析,帮助大家明白为何会有这么多的随机数算 ...

  2. 硬核 - Java 随机数相关 API 的演进与思考(下)

    本系列将 Java 17 之前的随机数 API 以及 Java 17 之后的统一 API 都做了比较详细的说明,并且将随机数的特性以及实现思路也做了一些简单的分析,帮助大家明白为何会有这么多的随机数算 ...

  3. Mahout推荐算法API详解

    转载自:http://blog.fens.me/mahout-recommendation-api/ Hadoop家族系列文章,主要介绍Hadoop家族产品,常用的项目包括Hadoop, Hive, ...

  4. 转】Mahout推荐算法API详解

    原博文出自于: http://blog.fens.me/mahout-recommendation-api/ 感谢! Posted: Oct 21, 2013 Tags: itemCFknnMahou ...

  5. C语言生成32位和64位随机数算法

    C语言生成32位和64位随机数算法 /** * randstd.h * * Standard definitions and types, Bob Jenkins * * 2015-01-19: re ...

  6. [转]Mahout推荐算法API详解

    Mahout推荐算法API详解 Hadoop家族系列文章,主要介绍Hadoop家族产品,常用的项目包括Hadoop, Hive, Pig, HBase, Sqoop, Mahout, Zookeepe ...

  7. Atitit.跨语言 java c#.net php js常用的codec encode算法api 兼容性  应该内置到语言里面

    Atitit.跨语言 java c#.net php js常用的codec encode算法api 兼容性  应该内置到语言里面 1. 常用算法1 1.1. 目录2 1.2. 定义和用法编辑2 1.3 ...

  8. Mahout推荐算法API具体解释【一起学Mahout】

    阅读导读: 1.mahout单机内存算法实现和分布式算法实现分别存在哪些问题? 2.算法评判标准有哪些? 3.什么会影响算法的评分? 1. Mahout推荐算法介绍 Mahout推荐算法,从数据处理能 ...

  9. 基于“均态分布”随机数算法的一次性口令OneTimePassword(原创)

    /* 所谓均态分布随机数算法是指:每个数(整数或实数)无序地分布在数轴上,值只出现一次永不重复.体现了香农的一次一密理论. * 均体现在每个数的值是平均概率,即都有出现:态体现在每个数在数轴上的位置是 ...

  10. java基础 - 冒泡排序,随机数算法

    从简单做起 任何困难的事情都是由简单的一步步一件件事情堆起来 理解好算法才是最重要 1.冒泡排序: public class Test { public static void main(String ...

随机推荐

  1. 【帆吖】Java学习零基础17

    Java方法 1 package method; 2 3 public class Demo1 { 4 //main方法 5 public static void main(String[] args ...

  2. java的死锁与解决方法

    一.什么是死锁? 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无限等待. 二.产生死锁的原因与四个条件 2.1 死锁原因 竞争资 ...

  3. Qt 5.15.2 QTextEdit无法设置新字体的处理方式

    首发于我的个人博客:xie-kang.com 博客内有更多文章,欢迎大家访问 原文地址 在使用QT 5.15.2 开发的过程中碰到了件怪事,下列代码无法给QTextEdit选中的文字设置字体: QTe ...

  4. 【Flutter】环境搭建(Windows+Android Studio 3.6.1)

    最近参加的项目需要用到Flutter框架进行iOS/Android双端开发,然而第一步环境搭建的过程就忙活了一整个晚上,直到现在终于有时间静下心来整理一下搭建过程中遇到的困难. 0x00 Flutte ...

  5. layui使用OSS上传

    1.首先要把aliyun-oss-sdk.js包下载下来,放到指定的目录下面  在要用的页面引入或者在index.html入口文件全局引入: <script src="util/ali ...

  6. 03-Spring使用注解方式注入

    基于注解的DI注入 1.导包 环境搭建:导入aop包(spring-aop-4.1.6.RELEASE.jar) 2.创建类 3. 创建spring.xml配置文件(必须在src目录下) 该配置文件与 ...

  7. 对Javaweb的相关练习之利用.jsp文件和.java文件将输入的数据存储到指定的数据库中

    练习分析 import javax.servlet.*; import javax.servlet.annotation.WebServlet; import javax.servlet.http.* ...

  8. CSAPP-Data Lab

    gcc -O1 -Wall -m32 -lm -o btest bits.c btest.c decl.c tests.c In file included from btest.c:16:0: /u ...

  9. 【事故】记一次意外把企业项目放到GitHub并被fork,如何使用DMCA下架政策保障隐私

    前言 缘由 在一个月黑风高的夜晚,正准备休息的我突然接到之前外包老总的亲切问候.一顿输出才知道三年前为了搭建流程化部署,将甲方的测试代码放到github上后忘记删除.现在被甲方的代码扫描机制扫到,并且 ...

  10. THFuse: An infrared and visible image fusion network using transformer and hybrid feature extractor 论文解读

    THFuse: An infrared and visible image fusion network using transformer and hybrid feature extractor ...