Sobol 序列并行化的实践经验

随机数发生器并行化的常见策略

随机数发生器的并行化通常有四种策略(文献【2】):

  1. “随机化”种子
  2. 参数化随机数发生器
  3. 分块策略
  4. 蛙跳策略

“随机化”种子

“随机化”种子就是让每个线程上运行的发生器使用不同的种子,这种策略常见于伪随机数。不过,使用不同随机数种子的发生器所产生的随机数是否依然保持统计意义上的独立性,这一点是未知的。

参数化随机数发生器

某些随机数发生器可以采用不同的参数配置,例如最简单的线性同余发生器,让每个线程运行不同参数配置的发生器就可以实现并行化。不过,这种策略和“随机化”种子有相同的安全隐患,无法确保产生的随机数依然保持统计意义上的独立性。另外,即使保持了独立性,可用的参数配置组合可能数量十分有限,无法充分发挥硬件的并行优势。

分块策略

要确保产生的随机数保持统计意义上的独立性,最简单的办法就是使用一个发生器,在不同的线程上返回不同的数字。要做到这一点可以采用分块生成的策略,下面举个例子。

无论是伪随机还是拟随机,发生器产生的随机数的顺序是不变的。假设要在 4 个线程上并行发生 10000 个随机数,分块策略就是让第 1 个线程返回第 1~2500 个随机数;第 2 个线程返回第 2501~5000 个随机数;第 3 个线程返回第 5001~7500 个随机数;第 4 个线程返回第 7501~10000 个随机数。

分块策略要求发生器必须有一个“长距离跳转”的功能,而且要保证计算成本尽量低。

蛙跳策略

想要使用一个发生器在不同的线程上返回不同的数字,还可以采用“蛙跳”的策略,下面举个例子。

假设要在 4 个线程上并行发生 10000 个随机数,蛙跳策略就是让第 1 个线程返回第 \(4k\) 个随机数;第 2 个线程返回第 \(4k + 1\) 个随机数;第 3 个线程返回第 \(4k + 2\) 个随机数;第 4 个线程返回第 \(4k + 3\) 个随机数。

同分块策略相比,每个线程上产生的数字是间隔开的,跳转步长等于线程数。

因为要多次跳转,蛙跳策略要求发生器必须有一个“极低成本”的“短距离跳转”的功能。

Sobol 序列的原理和跳转功能

Sobol 序列的产生依赖于一组事先确定“方向数”——\(m\),以及“异或”运算。对于多维 Sobol 序列,每个维度有各自的方向数。

Sobol 序列的发生可以表示为非递归和递归两种形式:

  • 非递归形式:\(y_n = g_1 m_1 \oplus g_2 m_2 \oplus g_3 m_3 \oplus \cdots\)
  • 递归形式:\(y_n = y_{n-1} \oplus m_{f(n-1)}, y_0 = 0\)

其中 \(\oplus\) 表示异或运算;\(g_i\) 是 \(n\) 的 Gray 码的二进制表示,\(n\) 的 Gray 码等于 \(n \oplus (n/2)\),而 \(n \oplus (n/2) = \dots g_3 g_2 g_1\);\(f(n)\) 表示 \(n\) 的二进制编码中最右侧的 0 所在的位数。

若采用 32 位整数,\(x_n = 2^{-32} y_n\),\(x_n\) 就是服从单位均匀分布的 Sobol 序列。

非递归形式表明 Sobol 序列有低成本的长距离跳转功能,因为若采用 32 位整数,直接计算 \(y_n\) 至多进行 32 次异或计算。计算 Sobol 序列的程序通常都会提供一个 skipTo 函数,实现长距离跳转。

Sobol 序列并行化实践

随机数的并行化要求精准的操作线程,因此在最常见的“单机多核”情形下可以采用 OpenMP 框架。

分块策略

假设已经具备了单线程上的 Sobol 序列发生器 SobolRsg,可以构造一个复合对象 OmpSobolRsg,内部包含 \(N\) 个完全相同的 SobolRsg 对象。在集中调用之前,事先告知 OmpSobolRsg 未来要调用的次数 \(M\),将 \(N\) 个 SobolRsg 对象分别跳转到合适的位置,第 \(i\) 个 SobolRsg 对象将专门在第 \(i\) 个线程上调用。这样就实现了 Sobol 序列的并行发生。

借助 OpenMP 框架,上述设计很容易实现,实现细节不赘述。

蛙跳策略

在蛙跳策略下,需要对 SobolRsg 对象稍加改造,以便实现从 \(y_{n}\) 直接到 \(y_{n+N}\) 的递归。和分块策略类似,需要构造一个复合对象 OmpSobolRsg,内部包含 \(N\) 个完全相同的 SobolRsg 对象。无需事先告知未来要调用的次数,只需初始化阶段将 \(N\) 个 SobolRsg 对象分别跳转,恰好错开一步。第 \(i\) 个 SobolRsg 对象将专门在第 \(i\) 个线程上调用。这样就实现了 Sobol 序列的并行发生。

借助 OpenMP 框架,上述设计也很容易实现,实现细节不赘述。

蛙跳策略的计算量分析

回想递归形式,在单线程情况下要生成 \(K\) 维的 Sobol 序列,需要进行 \(K\) 次整数间的异或计算,以及 \(K\) 次浮点数除法计算。蛙跳策略中的跳转则会触发若干次异或计算,当线程数量增多时,势必会增加计算量,削弱并行化的效果。

减少异或计算的技巧

依据递归形式,跳转 \(N\) 步的话有如下递归表达式:

\[y_n = y_{n-N} \oplus m_{f(n-N)} \oplus \cdots \oplus m_{f(n-2)} \oplus m_{f(n-1)}
\]

在原始情况下,跳转 \(N\) 步将发生 \(N\) 次异或计算。但根据 \(f(n)\) 的定义,\(m_{f(n-i)}\) 中可能有不少重合的数字。

与此同时,注意到异或计算的几个性质:

  • \(a \oplus b = b \oplus a\)
  • \(a \oplus (b \oplus c) = (a \oplus b) \oplus c\)
  • \(a \oplus a = 0\)
  • \(a \oplus 0 = a\)
  • \(0 \oplus 0 = 0\)

也就是说,偶数个 \(m\) 可以归并为 0,而 0 是可以忽略的。如果某个数字 \(m\) 出现了偶数次,可以直接忽略;如果某个数字 \(m\) 出现了奇数次,只保留一次就可以了。文献【1】中就举了这样的例子,跳转 \(2^p\) 步只需 2 次异或计算。

尽管如此,同分块策略相比蛙跳策略避免不了频繁跳转,也就避免不了多余的异或计算,这可能会导致并行 Monte Carlo 模拟的加速效率大打折扣。

分块策略不算缺点的缺点

和蛙跳策略相比,分块策略避免了反复跳转,但要求事先告知要调用的次数,这一点和随机数发生器通常的使用模式相背离。但是瑕不掩瑜,分块策略可以获得更好地加速比。笔者分别采用分块和蛙跳策略并行化了 QuantLib 的 Monte Carlo 定价框架,在 8 核 CPU 上,分块策略的加速比在 7 倍左右,而蛙跳策略的加速比在 5.4 倍左右。

图中可以看到,当线程数较少时,两种策略难分伯仲;但当线程数较多时,蛙跳策略的加速效率明显下降,这是频繁跳转造成的拖累。

参考文献

  • 【1】Parallelisation Techniques for Random Number Generators
  • 【2】Tina's Random Number Generator Library

Sobol 序列并行化的实践经验的更多相关文章

  1. CI Weekly #6 | 再谈 Docker / CI / CD 实践经验

    CI Weekly 围绕『 软件工程效率提升』 进行一系列技术内容分享,包括国内外持续集成.持续交付,持续部署.自动化测试. DevOps 等实践教程.工具与资源,以及一些工程师文化相关的程序员 Ti ...

  2. 根据实践经验,讲述些学习Java web能少走的弯路,内容摘自java web轻量级开发面试教程

    在和不少比较上进的初级程序员打交道的过程中,我们总结出了一些能帮到合格程序员尽快进阶的经验,从总体上来讲,多学.多实践不吃亏.本文来是从 java web轻量级开发面试教程从摘录的. 1  哪些知识点 ...

  3. 华为云对Kubernetes在Serverless Container产品落地中的实践经验

    华为云容器实例服务,它基于 Kubernetes 打造,对最终用户直接提供 K8S 的 API.正如前面所说,它最大的优点是用户可以围绕 K8S 直接定义运行应用. 这里值得一提是,我们采用了全物理机 ...

  4. 关于Flask使用Celery的实践经验分享

      最近大Boss反馈Celery经常出现问题,几经实践终于把问题解决了!于是乎有了这篇博客的诞生,算是一个实践经验的分享吧! 软件版本如下: Celery () Flask () RabbitMQ( ...

  5. 领域驱动设计(DDD)的实践经验分享之ORM的思考

    原文:领域驱动设计(DDD)的实践经验分享之ORM的思考 最近一直对DDD(Domain Driven Design)很感兴趣,于是去网上找了一些文章来看看,发现它确实是个好东西.于是我去买了两本关于 ...

  6. 领域驱动设计(DDD)的实践经验分享之持久化透明

    原文:领域驱动设计(DDD)的实践经验分享之持久化透明 前一篇文章中,我谈到了领域驱动设计中,关于ORM工具该如何使用的问题.谈了很多我心里的想法,大家也对我的观点做了一些回复,或多或少让我深深感觉到 ...

  7. 在单体应用的一些DDD实践经验

    阅读此文需要一定的DDD基础,如果你是第一次接触DDD读者,建议先去阅读一些DDD相关的书籍或者文章之后再来阅读本文. 背景 自从我在团队中推行DDD以来,我们团队经历了一系列的磨难--先是把核心项目 ...

  8. Java小白如何一步步学好Java,听听企业Java培训师的实践经验吧

    今天我准备给小主展示一篇Java培训老师的文章,希望能给Java小白一个学好Java的路径或者提示.以下就是原文: 从大学到现在,我使用Java已经将近20年,日常也带实习生,还在公司内部做train ...

  9. FreeWheel基于Go的实践经验漫谈——GC是大坑(关键业务场景不用),web框架尚未统一,和c++性能相比难说

    摘自:http://www.infoq.com/cn/news/2017/06/freewheel-experience-on-go Go语言是FreeWheel公司目前主要力推的一个方向,在其看来, ...

随机推荐

  1. Django——数据库连接配置

    配置settings.py : DATABASES = { 'default': { #default表示默认,也可以指定app 'ENGINE': 'django.db.backends.mysql ...

  2. NOIP模拟38:a

      这是T1.   考场上思路与正解就差个前缀,打的线段树,因为其巨大常数快乐挂掉......   正解复杂度是\(O(n^2m)\),其实再挂个\(log\)也能过,但是需要用常数极其优秀的树状数组 ...

  3. DH算法图解+数学证明

    前几天和同事讨论IKE密钥交换流程时,提到了Diffie-Hellman交换.DH算法最主要的作用便是在不安全的网络上成功公共密钥(并未传输真实密钥).但由于对于DH算法的数学原理则不清楚,因此私下对 ...

  4. L2TP协议简介

    传送门:L2TP代码实现 1. L2TP 概述 L2TP(Layer 2 Tunneling Protocol,二层隧道协议)是 VPDN(Virtual Private Dial-up Networ ...

  5. JS020. Array map()函数查到需要的元素时跳出遍历循环,不再执行到数组边界

    Array.prototype.map() map( )  方法创建一个 新数组 *,其结果是该数组中的每个元素是调用一次提供的 函数后的返回值 *.[ MDN / RUNOOB ] * map 添加 ...

  6. Ansible快速实战指南----多机自动化执行命令、部署神器

                                      1.需求: 需要在多台主机上,发送文件.执行命令,进行快速部署 2.ansible 远程复制文件 例子:在当前节点(20.88.14 ...

  7. 安装Centos7,出现无法联网的问题-----解决办法

    安装Centos7,出现无法联网的问题-----解决办法 我安装的是centos7的版本 在我照着centos7安装教程-CentOS-PHP中文网这个教程安装完后 我发现我的centOS无法联网,在 ...

  8. 第二十次CSP考试有感

    这是第二次参加csp考试了,大二上学期参加了第17次csp,160分.刚刚下午结束了第20次csp,200分. 这次比赛规则和以往不同,以前可以携带纸质书籍和usb,提交上去的答案不能实时出成绩.现在 ...

  9. 【tp3.2】根据不同域名来加载不同的配置文件

    遇到问题: 最近遇到一个需求,需要多个公众号使用同一个项目,这就导致了不同公众号访问的数据库和公众号配置不同. 解决思路: 查看文档:http://document.thinkphp.cn/manua ...

  10. 链式调用+对象属性与遍历+this指向+caller/callee

    之前的作业: 提示: 在开发的时候尽量在函数内部将作用都给调用好,在外部就能够直接使用 链式调用: 正常这样是不行的,因为没有具体返回值:  return 具体的对象,这样的才是链式操作,jquery ...