【问题标题】:Module loading with multiple processes多进程模块加载
【发布时间】:2014-10-04 17:56:11
【问题描述】:

我对 Julia 在使用工作进程时加载模块的行为感到困惑。

我需要使用一个相当重的PyPlot 模块,这需要相当长的时间来加载。这个程序:

using PyPlot
pygui(true)
println("Loaded")

在我的笔记本电脑上加载大约需要 11 秒:

% time julia test.jl 
INFO: Loading help data...
Loaded
julia test.jl  11,10s user 0,18s system 99% cpu 11,323 total

注意INFO: Loading help data... 行。它似乎是由 PyPlot 模块发出的,因为如果我省略 using PyPlot 行,它就不会出现。

但是,当我运行这个程序时:

using PyPlot
pygui(true)
@everywhere println("Loaded")

我得到了这些结果:

% time julia -p 4 test.jl 
INFO: Loading help data...
INFO: Loading help data...
INFO: Loading help data...
INFO: Loading help data...
INFO: Loading help data...
Loaded
    From worker 2:  Loaded
    From worker 5:  Loaded
    From worker 3:  Loaded
    From worker 4:  Loaded
julia -p 4 test.jl  88,94s user 1,19s system 266% cpu 33,865 total

它不仅运行了惊人的 33 秒(长了三倍!),而且它似乎还在每个工人身上加载 PyPlot 模块

但我确信,为了让每个工作人员都可以使用模块,它必须是@everywhered!确实,这个简单的程序崩溃了:

module Example
    export x
    x = 10
end

using Example

@everywhere println("x: $x")

调用:

% julia -p 4 test2.jl
x: 10
exception on 2: exception on exception on exception on 4: 5: 3: ERROR: x not defined
 in eval at /usr/bin/../lib/julia/sys.so
ERROR: x not defined
 in eval at /usr/bin/../lib/julia/sys.so
ERROR: x not defined
 in eval at /usr/bin/../lib/julia/sys.so
ERROR: x not defined
 in eval at /usr/bin/../lib/julia/sys.so

那么为什么即使我没有请求PyPlot 模块也会加载到所有工作人员身上?

更有趣的是,有一个解决方法:

using PyPlot
pygui(true)
addprocs(4)
@everywhere println("Loaded")

当我用julia test.jl 运行这个程序时,我得到了 15 秒:

% time julia test.jl     
INFO: Loading help data...
Loaded
    From worker 2:  Loaded
    From worker 4:  Loaded
    From worker 5:  Loaded
    From worker 3:  Loaded
julia test.jl  21,98s user 0,46s system 143% cpu 15,678 total

这正是我对使用julia -p 4 test.jl 运行的原始版本所期望的。但我不喜欢这种解决方法,因为它强制我的程序使用addprocs()

当 Julia 以 -p X 参数启动时,有没有办法限制模块加载到主进程?

【问题讨论】:

    标签: parallel-processing multiprocessing julia


    【解决方案1】:

    让我们使用DummyModule.jl进行测试:

    module DummyModule
    
    export MyType, f
    
    type MyType
        a::Int
    end
    
    f(x) = x^2+1
    
    println("loaded")
    
    end
    

    这样,我们可以看到实际上有 3 种可能性。所有这些实验都是从一个名为 julia -p 2 的新 julia 会话中运行的。

    在所有进程上使用 DummyModule

    julia> @everywhere using DummyModule
    loaded
            From worker 3:  loaded
            From worker 2:  loaded
    
    julia> @everywhere println(f(4))
    17
            From worker 2:  17
            From worker 3:  17
    
    julia> rr = RemoteRef(2)
    RemoteRef(2,1,24)
    
    julia> put!(rr, MyType(7))
    RemoteRef(2,1,24)
    
    julia> fetch(rr)
    MyType(7)
    

    仅在驱动进程上使用 DummyModule

    julia> include("DummyModule.jl")
    loaded
    
    julia> using DummyModule
    
    julia> @everywhere println(f(4))
    exception on 2: 17
    exception on 3: ERROR: f not defined
     in eval at /home/tim/src/julia/base/sysimg.jl:7
     in anonymous at multi.jl:1383
     in anonymous at multi.jl:819
     in run_work_thunk at multi.jl:592
     in run_work_thunk at multi.jl:601
     in anonymous at task.jl:6
    ERROR: f not defined
     in eval at /home/tim/src/julia/base/sysimg.jl:7
     in anonymous at multi.jl:1383
     in anonymous at multi.jl:819
     in run_work_thunk at multi.jl:592
     in run_work_thunk at multi.jl:601
     in anonymous at task.jl:6
    
    julia> rr = RemoteRef(2)
    RemoteRef(2,1,14)
    
    julia> put!(rr, MyType(7))
    WARNING: Module DummyModule not defined on process 2
    fatal error on 2: ERROR: DummyModule not defined
     in deserialize at serialize.jl:376
     in handle_deserialize at serialize.jl:351
     in deserialize at serialize.jl:505
     in handle_deserialize at serialize.jl:351
     in deserialize at serialize.jl:334
     in anonymous at serialize.jl:354
     in ntuple at tuple.jl:30
     in deserialize_tuple at serialize.jl:354
     in handle_deserialize at serialize.jl:346
     in anonymous at task.jl:824
    Worker 2 terminated.ERROR: ProcessExitedException()
     in wait at ./task.jl:284
     in wait at ./task.jl:194
     in wait_full at ./multi.jl:574
     in remotecall_fetch at multi.jl:675
     in remotecall_fetch at multi.jl:680
     in call_on_owner at multi.jl:722
     in put! at multi.jl:743
    

    (注意打印不同消息的顺序不是确定性的;在exception on 2: 17 中,exception ... 部分是由于从进程 2 发出的错误,17 来自打印 f(4) on过程 1.)

    仅在进程 1 上使用,但在工作人员之间传递数据

    julia> using DummyModule
    loaded
            From worker 3:  loaded
            From worker 2:  loaded
    
    julia> @everywhere println(f(4))
    exception on 2: 17
    exception on 3: ERROR: f not defined
     in eval at /home/tim/src/julia/base/sysimg.jl:7
     in anonymous at multi.jl:1383
     in anonymous at multi.jl:819
     in run_work_thunk at multi.jl:592
     in run_work_thunk at multi.jl:601
     in anonymous at task.jl:6
    ERROR: f not defined
     in eval at /home/tim/src/julia/base/sysimg.jl:7
     in anonymous at multi.jl:1383
     in anonymous at multi.jl:819
     in run_work_thunk at multi.jl:592
     in run_work_thunk at multi.jl:601
     in anonymous at task.jl:6
    
    julia> rr = RemoteRef(2)
    RemoteRef(2,1,19)
    
    julia> put!(rr, MyType(7))
    RemoteRef(2,1,19)
    
    julia> using DummyModule
    
    julia> fetch(rr)
    MyType(7)
    

    最后一种情况的区别在于,序列化程序实际上知道如何在所有进程上处理MyType,从而可以安全地在工作人员之间传递数据。

    【讨论】:

    • 更新:我改进了手册中的文档,见docs.julialang.org/en/latest/manual/parallel-computing/…
    • 谢谢你这么详细的解释,但我还是不明白为什么using一个模块只在主进程中强制所有工作人员加载这个模块。这似乎违反直觉,并且对于像 PyPlot 这样的大型模块会严重增加启动时间。
    • 是的,这是违反直觉且复杂的。是否有很多用于加载模块而不是将其纳入范围的用例?
    • @everywhere 也应与include("DummyModule.jl") 一起使用,以使模块对所有处理器可见。
    猜你喜欢
    • 1970-01-01
    • 2012-10-05
    • 1970-01-01
    • 2014-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-07
    相关资源
    最近更新 更多