Random 我们如何在Julia中并行生成随机数?

Random 我们如何在Julia中并行生成随机数?,random,parallel-processing,julia,Random,Parallel Processing,Julia,我正在为蒙特卡罗模拟编写一个并行julia代码。这需要我在不同的内核上并行生成随机数。在工作站上的一个简单测试代码中,我尝试在4个核上生成随机数,结果如下: julia -p 4 julia> @everywhere using Random julia> @everywhere x = randn(1) julia> remotecall_fetch(println,1,x[1]) -1.9348951407543997 julia> remotecall_fe

我正在为蒙特卡罗模拟编写一个并行julia代码。这需要我在不同的内核上并行生成随机数。在工作站上的一个简单测试代码中,我尝试在4个核上生成随机数,结果如下:

julia -p 4

julia> @everywhere using Random

julia> @everywhere x = randn(1)

julia> remotecall_fetch(println,1,x[1])
-1.9348951407543997

julia> remotecall_fetch(println,2,x[1])
      From worker 2:    -1.9348951407543997

julia> remotecall_fetch(println,3,x[1])
      From worker 3:    -1.9348951407543997

julia> remotecall_fetch(println,4,x[1])
      From worker 4:    -1.9348951407543997
我不明白为什么从不同的进程中获取的数字给出完全相同的结果。我不确定是什么错误。我的理解是,使用@everywhere宏可以在所有进程上并行运行同一段代码。我目前在我的电脑上是julia 1.6.0。多谢各位


更新:感谢您的回复。基本上,我要寻找的是一个赋值语句,如x=y,其中x和y都是工作进程的局部。我试过这样的方法:

julia -p 4

@sync @distributed for i = 1:2
       x = randn(1)
       println(x)
       end
      From worker 3:    [0.4451131733445428]
      From worker 2:    [-0.4875627629008678]
Task (done) @0x00007f1d92037340

julia> remotecall_fetch(println,2,x)
ERROR: UndefVarError: x not defined
Stacktrace:
 [1] top-level scope
   @ REPL[23]:1
这似乎在每个进程上独立地生成随机数。但是,我不知道如何访问变量
x
。我尝试了
remotecall\u fetch(println,2,x)
但是变量
x
似乎没有在工作进程上定义。这让人非常困惑


我希望有好的流程图或好的文档来解释并行计算期间Julia中变量和表达式的范围。

remotecall\u fetch
发送
x[1]
,以便从本地进程进行评估(id为1)。您可以通过运行以下代码进行检查:

# julia -p 4

julia> @everywhere x = myid() # make sure x holds a worker number

julia> remotecall_fetch(println, 4, x) # x passed from worker 1 (local machine) to println
      From worker 4:    1

julia> @sync @everywhere println(x) # x is evaluated on worker
1
      From worker 3:    3
      From worker 2:    2
      From worker 4:    4
      From worker 5:    5

julia> @sync @everywhere println($x) # x interpolated from local machine
1
      From worker 4:    1
      From worker 5:    1
      From worker 3:    1
      From worker 2:    1


关于远程机器上的随机数生成,您应该确保在每个机器上创建独立的随机数流。对于大多数情况来说,最简单的方法就是使用
Random.seed在不同的工人身上使用不同的种子。如果您想格外小心,请使用
Future.randjump
来确保工作进程上的随机数生成器没有重叠。

最好的方法是使用单个随机状态,并在每个工作进程都有一个片段的位置将其切片。这可以通过以下方式实现:

using Distributed
addprocs(4)
@everywhere import Future, Random
@everywhere const rng = Future.randjump(Random.MersenneTwister(0), myid()*big(10)^20)
现在,在每个worker上都有一个worker本地变量,但在Julia意义上是全局
rng
变量

在本例中,我使用了
0
ad随机数种子。手册中建议使用
randjump
大小
big(10)^20
,因为在Julia中,此步骤已经预先计算了值

要使用此类rng,您可以定义如下函数:

@everywhere getr(rng=rng) = rand(rng, 5)
可以称之为

fetch(@spawnat 2 getr())

基本上是
rng
,因为它是
global
,所以应该将它作为最外层的参数传递给您正在调用远程工作者的任何对象,或者定义为
const
,如注释中所述。

您在问题的顶部说“在不同的内核上”。 这就是我使用
Threads.@thread
宏在基于线程的并行上生成随机数的函数所使用的解决方案。它的优点是,我可以根据运行代码时的需要选择所需的随机性级别:

using Random, Test, Statistics

FIXEDRNG = MersenneTwister(123)

println("** Testing generateParallelRngs()...")
x = rand(copy(FIXEDRNG),100)

function innerExpensiveFunction(bootstrappedx; rng=Random.GLOBAL_RNG)
     sum(bootstrappedx .* rand(rng) ./ 0.5)
end
function outerFunction(x;rng = Random.GLOBAL_RNG)
    masterSeed = rand(rng,100:9999999999999) 
    rngs       = [deepcopy(rng) for i in 1:Threads.nthreads()]  # make new copy instances
    results    = Array{Float64,1}(undef,30)
    Threads.@threads for i in 1:30
        tsrng         = rngs[Threads.threadid()]    # Thread safe random number generator: one RNG per thread
        Random.seed!(tsrng,masterSeed+i*10)         # But the seeding depends on the i of the loop not the thread: we get same results indipendently of the number of threads
        toSample      = rand(tsrng, 1:100,100)
        bootstrappedx = x[toSample]
        innerResult   = innerExpensiveFunction(bootstrappedx, rng=tsrng)
        results[i]    = innerResult
    end
    overallResult = mean(results)
    return overallResult
end


# Different sequences..
@test outerFunction(x) != outerFunction(x)

# Different values, but same sequence
mainRng = copy(FIXEDRNG)
a = outerFunction(x, rng=mainRng)
b = outerFunction(x, rng=mainRng)

mainRng = copy(FIXEDRNG)
A = outerFunction(x, rng=mainRng)
B = outerFunction(x, rng=mainRng)
@test a != b && a == A && b == B


# Same value at each call
a = outerFunction(x,rng=copy(FIXEDRNG))
b = outerFunction(x,rng=copy(FIXEDRNG))
@test a == b

是的,这就是我所说的使用
Future.randjump
写作的意思。通常,
rng
将被定义为
const
,以避免Przemysław所写的成本。感谢您的回复。基本上,我要寻找的是一个赋值语句,如x=y,其中x和y都是工作进程的局部。我试过这样的方法:
julia-p2
x=randn(1)
end
这似乎在每个进程上独立地生成随机数。但是,我不知道如何访问变量
x
。我尝试了
remotecall\u fetch(println,2,x)
但是变量
x
似乎没有在工作进程上定义。这太让人困惑了。我想你需要一个单独的问题和一个MWE。ParallelDataTransfer包非常适合在工作人员之间移动数据。如果您不需要传输数据,您应该将所有内容封装在一个函数中,然后远程调用该函数。对于多线程场景,此代码不正确(行太多,使用randjump是首选方式),请参阅@Przemyslaw Szufel:我不知道您所说的“行太多,因此不正确”是什么意思。关于“radjump”,并不是所有RNG都能实现它(如Stablerns没有)@Antonello,谢谢你的回复。多线程让我有点焦虑,因为共享内存,尽管我对此经验很少。我对分布式系统有更多的经验,其中每个进程都有自己的内存块。我已经在fortran中使用MPI很长一段时间了,实现起来非常简单。Julia中的约定似乎有点不同。@Antonello您首先构建RNG的
向量
,然后对其重新设定种子,这基本上与从头开始重新生成它们一样多。您应该执行类似于
Vector{MersenneTwister}(unde,Threads.nthreads())
的操作,然后在线程中填充值。关于
randjump
,我同意,然而,大量的RNG具有相同的功能,为了避免随机流中的任何相关风险,这些风险可能会给您的模拟带来伪影,您应该选择一个具有时间效率跳跃的RNG。@Przemyslaw:我确实对RNG进行了深度复制,然后重新设定“已分配”线程的种子基于每个迭代id(而不是线程id)执行迭代。这保证了我的结果不受我使用的线程数量的影响,并且,在我的例子中,与主计算相比,它的计算成本可以忽略不计。我没有观察到伪影(除了对某些RNG使用非常小的种子数),但如果您有更好的解决方案,我是“接收者”。。。