parallel-processing - Julia:并行代码比顺序代码慢,有remotecall()的替代方法吗?

标签 parallel-processing julia

当我并行运行一个简单函数(每个工作线程具有不同的输入)时,并行版本代码所需的时间始终比顺序运行的时间长。

简单的功能是

addprocs(2)
@everywhere function simple(i,Q,vt_0,ct_0)
  a=sum((rand(1:i),vt_0,ct_0))*2
  return a
end

我的代码的并行化版本是

#In Parallel
tic()
N=10000;
v=zeros(N);
c=zeros(N);
vt_0=0;
ct_0=0;

for i=1:N

  v[i]=fetch(remotecall(simple,2,i,1,vt_0,ct_0))

  c[i]=fetch(remotecall(simple,3,i,2,vt_0,ct_0))

  vt_0=v[i]
  ct_0=c[i]

end
toc()

虽然顺序代码只是在循环的每次迭代中调用函数(没有 remote call() 也没有 fetch(),但我正在考虑这样的方式我在 Julia 中使用 remotecall() 并行调用 worker 是问题的根源,但我不确定。

任何人都可以帮我弄清楚为什么这个并行代码与调用函数相比如此慢?或者这个简单的计算不值得并行化吗?

编辑:
这是顺序代码:

#Sequential
tic()
N=10000;
v=zeros(N,2);
c=zeros(N,2);
vt_0=0;
ct_0=0;

for i=1:N

  v[i]=simple(i,1,vt_0,ct_0)

  c[i]=simple(i,2,vt_0,ct_0)

  vt_0=v[i]
  ct_0=c[i]
end
toc()

最佳答案

即使对于 simple() 内的非常“浅”计算的情况,Julia 文档也建议使用 remotecall_fetch() 而不是 fetch(remotecall( ... ) ):

The function remotecall_fetch() exists for this purpose. It is equivalent to fetch(remotecall(...)) but is more efficient.


[PARALLEL]-进程@spawn/fetch()管理费用

这是一个经常被忽视的主题,积极认识并尝试推理这个主题非常好。实际上,这是对[PARALLEL]进程调度最终性能产生不利影响的常见根本原因。 If indeed interested in details, getting both the mathematical model + an interactive UI-tool to simulate / evaluate the net-effects of the overhead-strict speedup formula on [PARALLEL] code-executions for as much as 2 .. 8000+ CPU-cores, feel free to read more on this here


这里主要的“可疑”是值进程间依赖关系:

  • 其中一个可能隐藏在系统函数rand()内部。如果使用加密强实现,则每次对 rand() 的调用都必须更新中央随机源状态。这意味着,由于这个特殊原因,所有、实际上所有生成的进程都必须建立并维护一个到该中央共享服务的队列,该队列根本不会出现(因为它可以使零源)随机状态更新麻烦)在一个简单的[SEQ]-代码执行中,但需要一些额外的隐藏开销(MUTEX/LOCKS/value-updateatomics等)在 [PAR] 代码执行单元的情况下,实际上“共享”此中央资源(被隐藏为严格的事务性,并发容量为 1 ,操作 1:N 阻塞逻辑信令,无回滚...)并且必须在下次调用随机源之前执行并强制执行随机源状态的原子安全更新' 服务可能会得到提供。

  • 另一个是 {prior|next} 循环步骤依赖性,在对 simple() 流程实例的调用对之间多次相互交叉。如下图所示,这种相互依赖实际上使得所有潜在的派生调用都必须排列为纯 [SEQ]-process-schedule,而“剩余”sum() [PARALLEL]-流程调度中确实没有多少肉可咀嚼。


实际的进程间相互依赖
插图描述了至少不可能注入(inject)循环
以便使[并行]-处理更高效:

//                           +-------------<---- a way to "inject" a for-loop
//                           |  +----------<---- NOT CONSUMED AT ALL
//                           |  |
//                           |  |             +--******* BUT ********* DEPENDENCY
//                           |  |             |
//                           |  |             v
//                           |  |  +-------<-->- v[]-{prior|next}-step DEPENDENCY
//                           |  |  |     +-<-->- c[]-{prior|next}-step DEPENDENCY
//                           |  |  |     |
//          function simple( i, Q, vt_0, ct_0 )
@everywhere function simple( N, Q, vt_0, ct_0, aVEC )
   for     i  = 1:N
      aVEC[i] = sum( (  rand(1:i), vt_0, ct_0 ) )*2
      //   a  = sum( (  rand(1:i), vt_0, ct_0 ) )*2
      // ret a
end

通过 { put!() | 添加具有显式进程间通信的 CSP channel 时take!() } channel 方法可以解决这种正在通信的依赖关系,猜猜看,协程调度只会增加额外的开销,因此期望付出更多以获得更少。


关于原始分析的小注释:

在所有情况下,建议将 tic() .. toc() 括号紧贴在测试的代码部分,并避免和排除实际测量执行中的任何和所有内存分配以及类似的极长且嘈杂的部分:

// ------------------------------------------------------------<SuT>-START
tic()
for i=1:N

  v[i]=fetch(remotecall(simple,2,i,1,vt_0,ct_0))
  c[i]=fetch(remotecall(simple,3,i,2,vt_0,ct_0))

  vt_0=v[i]
  ct_0=c[i]

end
toc()
// ------------------------------------------------------------<SuT>-FINISH

关于parallel-processing - Julia:并行代码比顺序代码慢,有remotecall()的替代方法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46941164/

相关文章:

c++ - 跨越多个函数/对象的 OpenMP 并行区域

parallel-processing - 为什么摩尔定律需要并行计算?

arrays - 从 Julia 的字母表中有效地生成长度为 n 的所有单词的集合

julia - 在 Julia 中模仿 Scala Option[T] 的最佳方式?

Julia 中瓶颈操作的优化

julia - 获取Julia中p个最大特征值对应的特征向量

r - 与 R 并行置换矩阵列

java - Maven surefire 测试重用 fork 与 parallel

c - 关键部分中的函数在 OpenMP 中产生数据竞争

dataframe - 用 Julia Dataframe 中另一列的值替换缺失值