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. 云原生 AI 前沿:Kubeflow Training Operator 统一云上 AI 训练

    分布式训练与 Kubeflow 当开发者想要讲深度学习的分布式训练搬上 Kubernetes 集群时,首先想到的往往就是 Kubeflow 社区中形形色色的 operators,如 tf-operat ...

  2. K8S的部署方式

    K8S部署主要有两种方式:

  3. Linux 学习路线

    前言 这篇文章会一直更新...只是将个人的文章总结归纳到这,不代表最佳学习路线 没有链接的文章后续会补上...还没写的知识点未来用到也会补上...太卷了 常用基础命令 Linux常用命令 - cd命令 ...

  4. IIS托管Asp.net Core及Abp VNext

    默认方式安装IIS后,从官方网站下载IIS模块 https://dotnet.microsoft.com/download/dotnet-core/3.1 2个都需要安装 安装后,新建网站指向发布的磁 ...

  5. 写了一年golang,来聊聊进程、线程与协程

    本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star. 进程 在早期的单任务计算机中,用户一次只能提交一个作业,独享系统的全部资源,同时也只能干一件事 ...

  6. Docker Compose 实践及梳理

    Docker Compose 可以实现 Docker 容器集群的编排,可以通过 docker-compose.yml 文件,定义我们的服务及其需要的依赖,轻松地运行在测试.生产等环境 文档 Produ ...

  7. js设计模式之发布订阅模式

    1. 定义 发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知. 订阅者(Subscriber)把自己想订阅的事件注册(Subscri ...

  8. EcShop调用显示指定分类下的子分类方法

    ECSHOP首页默认的只有全部分类,还有循环大类以及下面小类的代码,貌似没有可以调用显示指定大类下的子分类代码.于是就有这个文章的产生了,下面由夏日博客来总结下网站建设过程中ECSHOP此类问题的网络 ...

  9. Docker系列(22)- DockerFile指令说明并构建自己的centos

    DockerFile常用指令 实战测试 DockerHub中99%镜像都是从这个基础镜像过来的FROM scratch,然后配置需要的软件和配置来进行构建 #创建一个自己的centos # 1.编写D ...

  10. $.ajax 常用的套路

    $.ajax 常用的套路 (function(){ window.webApi = new Object(); webApi.get = function(url,data,callback){ $. ...