GHC的thunk如何是primefaces的?
GHC如何处理由多个线程访问的thunk(显式线程或者评估火花的内部线程)? multithreading可以评估同一个thunk,重复工作吗? 或者,如果thunk是同步的,怎么样,这样的performance不受影响?
从链接的博客文章 @Lambdageek, GHC评论和GHC用户指南我拼凑在一起:
GHC试图防止重新评估thunk,但是因为线程之间的真正locking是昂贵的,而thunk通常是纯净的,所以对于重新评估是无害的,通常这样做是马虎的,不pipe怎样重复工作的机会很小 。
它用来避免工作的方法是用一个黑洞 (一个告诉其他线程(或者有时是线程本身;这就是<<loop>>
检测是如何发生的))的特殊标记来代替thunk。
鉴于此,至less有三种select:
-
默认情况下,它使用“lazy blackholding”,这只在线程即将暂停之前完成。 然后“走”它的堆栈,并为新的“thunk”创build“真正的”blackholes,使用locking来确保每个thunk只有一个线程拒绝它,如果发现另一个线程已经破坏了thunk,则放弃自己的评估。 这样比较便宜,因为它不需要考虑评估时间如此短以至于完全适合两次暂停之间的评估。
-
使用
-feager-blackholing-flag
,只要一个thunk开始评估,就会创buildblackholes,而如果你做了很多并行操作,用户指南build议这样做。 然而,因为locking每一个thunk会太昂贵,这些blackholes是便宜的“渴望的”,这是不同步的其他线程(虽然其他线程仍然可以看到他们,如果没有竞争条件)。 只有当线程暂停时,才会变成“真正的”黑洞。 -
第三种情况,博客文章特别关注,用于特殊function,比如
unsafePerformIO
,对于这种情况不止一次评估thunk是有害的 。 在这种情况下,线程使用真正locking的“真实”黑洞,但是通过在真正的评估之前插入人为线程暂停来立即创build线程。
总结在评论中链接的文章:GHC中的thunk在多个线程之间不是严格primefaces的:在multithreading竞争条件下可能评估相同的thunk,复制工作是可能的。 但是,这在实践中并不是什么大问题,因为:
- 保证的纯度意味着一个thunk是否被评估了两次,从程序语义的angular度来说,这并不重要。
unsafePerformIO
就是这种情况,这可能是一个问题,但事实certificateunsafePerformIO
很小心避免重新运行相同的IO操作。 -
因为所有的值都是thunk,所以大部分的thunk是相当小的,所以偶尔重复这个工作来强制一个在编程速度方面没有什么大不了的。 你可能会想象,重复的代价很高,例如,
last [1,2..10000000]
因为整个计算是昂贵的。 但是当然,真正的最后的thunk只是解决了另一个thunk,像是:case [1,2..10000000] of [x] -> x (_:xs) -> last xs [] -> error "empty list"
如果两个线程重复工作,把这个调用
last
转化为一个使用case
,这在事情的macros观计划中是相当便宜的。
是的,有时相同的thunk可以由多个线程评估。 GHC运行时会尽量减less重复工作的可能性,所以在实际中很less见。 请参阅“共享内存多处理器上的Haskell”以了解更低级别的细节,大多数是“无锁Thunk评估”部分。 (我推荐每个专业的haskell开发者的论文btw。)