Julia中并行实现比串行实现更慢

6
为什么下面的Julia代码并行实现运行速度比串行实现慢?
using Distributed

@everywhere function ext(i::Int64)
   callmop = `awk '{ sum += $1 } END { print sum }' infile_$(i)`
   run(callmop)
end

function fpar()
   @sync @distributed for i = 1:10
      ext(i)
   end
end

function fnopar()
   for i = 1:10
      ext(i)
   end
end

val, t_par, bytes, gctime, memallocs = @timed fpar()
val, t_nopar, bytes, gctime, memallocs = @timed fnopar()

println("Parallel: $(t_par) s. Serial: $(t_nopar) s")  
# Parallel: 0.448290379 s. Serial: 0.028704802 s

infile_$(i)文件包含单列实数。经过一些研究,我遇到了这个帖子和这个其他帖子),它们处理类似的问题。然而,如果考虑Julia正在快速发展的速度,它们似乎有点过时。有没有办法改进这个并行部分?非常感谢您的帮助。


在我看来,这不是测试并发性的最佳示例。生成外部进程并同时访问同一文件(文件/文件IO也有锁)会同时打开太多问题,并且会使事情变得模糊。此外,工作量(10个数字)太小,多线程的开销不太可能被摊销。更好的方法是将更多数据读入数组,然后使用该数组来比较单线程和多线程执行。 - BitTickler
@BitTickler 感谢您的评论。上面的代码是对实际问题的简化:我绝对需要使用输入文件调用外部进程(实际上,我调用了量子化学软件包[MOPAC](http://openmopac.net/)),由于所有输入都是独立的,因此我认为这是一个令人尴尬的并行问题。我使用`awk`命令,因为它与我所处的情况有一些相似之处。 - panadestein
1个回答

7

你的代码是正确的,但是你对性能的测量方式不正确。

请注意,对于这种用例场景(调用外部进程),你应该可以使用绿色线程 - 没有必要分配负载!

当 Julia 函数第一次执行时,它会被编译。当你在几个并行进程上执行它时,所有进程都需要编译相同的代码。

除此之外,在第一次运行@distribution 宏时,编译时间也比较长。因此,在使用@timed 之前,你应该先调用一次 fparnofpar 函数。

最后,你的代码中没有 addprocs ,但我假设你使用了 -p Julia 选项将 worker 进程添加到你的 Julia 主进程中。顺便说一下,你没有提到你有多少个 worker 进程。

通常我会像这样测试代码:

@time fpar()
@time fpar()
@time fnopar()
@time fnopar()

第一步是了解编译时间,第二步是了解运行时间。
另外,值得一提的是,需要查看 "BenchmarkTools" 包和 "@btime" 宏。
关于性能测试,"@distributed" 存在显著的通信开销。在某些情况下,可以通过使用 "SharedArrays" 进行缓解,在其他情况下则可以使用 "Thread.@threads"。但是在你的情况下,最快的代码将是使用 green threads 的代码:
function ffast()
   @sync for i = 1:10
      @async ext(i)
   end
end

非常感谢您的快速回答和有趣的评论。确实,我使用-p标志来设置进程数。那么您建议如何更好地测试性能呢? - panadestein
你是绝对正确的,如果不考虑编译时间,那么并行实现会更快。然而,使用8个进程,性能提升相对较小。并行0.014887秒与串行0.031449秒。我认为如果考虑开销,这已经是最好的结果了。 - panadestein
分布式性能存在许多问题,例如通信时间昂贵。尝试使用绿色线程来实现我的代码建议,并告诉我你得到了什么 :-) - Przemyslaw Szufel
2
结果:并行:0.010793981秒。串行:0.031697373秒。绿色:0.007791053。绿色线程是最好的解决方案。非常感谢 :) - panadestein

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接