【发布时间】:2010-09-05 15:11:00
【问题描述】:
除了手动添加 PLinq 扩展语句 AsParallel() 之外,编译器不能自动为我们解决这个问题吗?如果代码支持并行化,是否有任何您特别不希望并行化的示例?
【问题讨论】:
除了手动添加 PLinq 扩展语句 AsParallel() 之外,编译器不能自动为我们解决这个问题吗?如果代码支持并行化,是否有任何您特别不希望并行化的示例?
【问题讨论】:
我在自动并行化领域进行研究。这是一个非常棘手的话题,是许多博士的主题。论文。静态代码分析和自动并行化在 Fortran 等语言中取得了巨大成功,但编译器分析区域间依赖关系的能力受到限制。大多数人不愿意牺牲代码正确性的保证来换取潜在的并行性能提升,因此编译器在插入并行标记的位置必须相当保守。
底线:是的,编译器可以并行化代码。但是人类通常可以更好地并行化它,并且让编译器找出将标记放在哪里可能非常非常非常棘手。有数十篇关于该主题的研究论文,例如 Mitosis 并行编译器的背景工作或 D-MPI 工作。
【讨论】:
自动并行化比最初看起来要复杂。正是“如果代码支持它”部分让您受益匪浅。考虑类似
counter = 0
f(i)
{
counter = counter + 1
return i + counter
}
result = map(f, {1,2,3,4})
如果编译器只是决定在这里并行化 map,那么每次运行程序时可能会得到不同的结果。当然,很明显 f 实际上并不支持以这种方式使用,因为它有一个全局变量。但是,如果 f 在不同的程序集中,编译器无法知道并行化它是不安全的。它可以在运行时自省 f 所在的程序集并随后做出决定,但随后它变成了一个问题:“自省是否足够快,动态并行化是否足够快,不会否定这样做的好处?”有时可能无法自省,f 可能是一个 P/Invoked 函数,它实际上可能非常适合并行化,但由于运行时/编译器无法知道它必须假设它不能.这只是冰山一角。
简而言之,这是可能的,但很困难,因此在实施魔法和魔法的好处之间进行权衡很可能在错误的方向上偏离得太远了。
【讨论】:
好消息是编译器研究人员说我们真的接近于自动并行化编译器。坏消息是他们已经说了五十年了。
C# 等不纯语言的问题通常是没有足够的并行性。在不纯的语言中,人类很难,而且程序几乎不可能弄清楚两段代码是否以及如何相互或与环境交互。
在纯粹的、引用透明的语言中,您会遇到相反的问题:一切都是可并行化的,但它通常没有意义,因为调度线程比仅仅执行该死的。
例如,在一个纯粹的、引用透明的、函数式语言中,如果我有这样的东西:
if a <= b + c
foo
else
bar
end
我可以启动五个线程并并行计算 a、b、c、foo 和 bar,然后我计算 +,然后是 <=,最后,我计算if,这仅仅意味着丢弃foo 或bar 的结果,这两者都已经计算过了。 (请注意,这取决于功能性:在不纯的语言中,您不能简单地计算 if 的两个分支然后丢弃一个。如果两者都打印一些东西怎么办?您将如何“取消打印”它?)
但是如果a、b、c、foo和bar真的很便宜,那么这五个线程的开销将远远大于计算本身。
【讨论】: