我是一个中级Haskell程序员,在严格的FP和非FP语言方面有丰富的经验。我的大部分Haskell代码分析了中等规模的数据集(10^6.10^9),所以懒惰总是潜伏着的。我对数据块、WHNF、模式匹配和共享有相当好的理解,并且我已经能够用爆炸模式和seq修复漏洞,但是这种profile和祷告的方法感到肮脏和错误。
我想知道在设计时,Haskell程序员是如何对待懒惰的,。我不是在问像Data.ByteString.Lazy或foldl‘这样的简单项目,而是想知道您是如何看待导致运行时内存问题和棘手调试的低级懒惰机器的。
在设计期间,您是如何看待块、模式匹配和共享的?
您使用什么设计模式和习惯用法来避免泄漏?
你是如何学习这些模式和习语的,你有一些好的参考资料吗?
如何避免无泄漏问题的过早优化?
(在时间预算编制方面,修订了2014-05-15 ):
您是否为查找和修复内存问题编列了大量的项目时间?
或者,您的设计技能通常避免内存问题,并且在开发周期的早期就获得了预期的内存消耗吗?
发布于 2014-04-25 00:21:39
我认为大多数“严格泄漏”的问题发生是因为人们没有一个好的概念模型。没有一个好的概念模型的Haskellers倾向于并传播一种迷信,即更加严格是更好的。也许这种直觉来自于他们通过玩弄小例子而产生的结果&紧密的循环。但这是不正确的。在适当的时候懒惰和在正确的时间严格要求同样重要。
有两类数据类型,通常称为“数据”和“共同数据”。必须尊重每个人的模式。
X获取评估“的形式,而是”当Y被计算时,X也是“)。filter一样)。codata的另一个重要部分是尽可能线性地使用它--也就是精确地使用列表的尾部一次;使用树的每个分支精确地使用一次。这确保GC可以在使用时收集碎片。当你有包含数据的共同数据时,事情需要特别小心。例如,iterate (+1) 0 !! 1000最终会在评估它之前产生一个1000磅的大小。您需要再次考虑条件严格性--防止这种情况的方法是确保当列表的一个缺点被消耗时,就会添加它的元素。iterate违反了这一点,所以我们需要一个更好的版本。
iterate' :: (a -> a) -> a -> [a]
iterate' f x = x : (x `seq` iterate' f (f x))当然,当你开始写东西的时候,很难判断什么时候会发生不好的事情。通常,很难使高效的数据结构/函数在数据和协同数据上同样工作良好,而且重要的是要记住哪一个(即使在多态设置中,在没有保证的情况下,您应该考虑并努力尊重它)。
分享是一个棘手的问题,我认为我主要是在逐个案例的基础上来处理这个问题。因为这很棘手,所以我尽量将其本地化,选择不向一般的模块用户公开大型数据结构。这通常可以通过公开用于生成所述事物的组合子来完成,然后一次产生和使用所有这些(单子上的共密度转换就是一个例子)。
我的设计目标是让每个函数都尊重我的类型的数据/协数据模式。我通常能做到这一点(虽然有时需要一些深思熟虑-这些年来,它已经变得很自然了),而且我很少有泄漏问题。但我并不认为这很容易--它需要掌握规范库和语言模式的经验。这些决定不是孤立地作出的,一切都必须立即正确,才能顺利运作。一台调得不好的乐器会毁了整个音乐会(这就是为什么“随机扰动优化”几乎从不适用于这类问题的原因)。
Apfelmus的空间不变量文章有助于进一步发展您的空间/直觉。也见下面爱德华·克米特的评论。
https://stackoverflow.com/questions/23280936
复制相似问题