2012-09-21 35 views
15

我对haskell有一定的理解,但我总是很少不确定应该使用什么样的编译指示和优化,以及在哪里。像何时使用各种语言编译指示和优化?

  • 像什么时候使用SPECIALIZE编译指示和它有什么样的性能增益。
  • 使用地点RULES。我听说有人采取特定的规则不开火?我们如何检查?
  • 什么时候使函数的参数严格,什么时候可以提供帮助?我明白,严格论证会使参数被评估为正常形式,那么为什么我不应该对所有函数参数加严格?我如何决定?
  • 如何查看和检查我的程序中是否有空间泄漏?什么是构成空间泄漏的一般模式?
  • 我该如何看看是否有太多懒惰的问题?我总是可以检查堆分析,但我想知道什么是懒惰伤害的一般原因,示例和模式?

是否有任何关于高级优化(包括更高和更低级别)的源代码,特别是对于haskell?

+0

RHW CH 25? http://book.realworldhaskell.org/read/profiling-and-optimization.html –

+0

@DonStewart谢谢..我已经看过RWH ..问题是我不知道他们做什么,但我并没有对当直觉以及他们的使用是重要的而不重要的地方。我问了这个问题找出其他来源,以进一步加深我的理解。 – Satvik

回答

18

像什么时候使用SPECIALIZE编译指示和它的性能增益。

如果您有一个(类型)多态函数,并且希望它经常在类的一个或几个实例上调用,那么让编译器专门化一个函数。

专业化将字典查找删除使用的位置,并且通常可以进一步优化,类成员函数通常可以内联,然后进行严格分析,两者都可能带来巨大的性能提升。如果唯一可能的优化是消除查询,那么收益一般不会很大。

由于GHC-7,它可能更有效地让功能的{-# INLINABLE #-}编译,这使得在接口文件中提供的(几乎没有变化,进行一些规范和脱糖)源,因此函数可以专门和甚至可能在呼叫站点内联。

在哪里可以使用RULES。我听说有人采取特定的规则不开火?我们如何检查?

您可以使用-ddump-rule-firings命令行选项来检查哪些规则已被触发。这通常会抛出大量已解除规则,因此您必须为自己的规则搜索一下。

您使用规则

  • 当你有一个功能更有效的版本,特殊工种,如

    {-# RULES 
    "realToFrac/Float->Double" realToFrac = float2Double 
        #-} 
    
  • 当某些功能可以被更有效的版本替换为特殊参数时,例如,

    {-# RULES 
    "^2/Int"  forall x. x^(2 :: Int) = let u = x in u*u 
    "^3/Int"  forall x. x^(3 :: Int) = let u = x in u*u*u 
    "^4/Int"  forall x. x^(4 :: Int) = let u = x in u*u*u*u 
    "^5/Int"  forall x. x^(5 :: Int) = let u = x in u*u*u*u*u 
    "^2/Integer" forall x. x^(2 :: Integer) = let u = x in u*u 
    "^3/Integer" forall x. x^(3 :: Integer) = let u = x in u*u*u 
    "^4/Integer" forall x. x^(4 :: Integer) = let u = x in u*u*u*u 
    "^5/Integer" forall x. x^(5 :: Integer) = let u = x in u*u*u*u*u 
        #-} 
    
  • 重写根据一般规律可能会产生的代码是更好地优化,例如表达式时

    {-# RULES 
    "map/map" forall f g. (map f) . (map g) = map (f . g) 
        #-} 
    
在后者的样式

广泛使用的RULES在融合框架制成,例如在text库,并且该列表的功能在base中,不同种类的融合(foldr/build融合)的实施使用规则。

什么时候使函数的参数严格,什么时候可以提供帮助?我明白,严格论证会使参数被评估为正常形式,那么为什么我不应该对所有函数参数加严格?我如何决定?

制作一个参数严格将确保其被评估到弱头部正常形态,不正常的形式。

你不要让所有的参数严格,因为有些功能必须是非严格一些他们的论据在所有的工作,有些是如果不太严格高效的所有参数。

对于examplepartition必须是非严格在其第二个参数上无限列表在所有的工作,更一般在foldr使用必须是非严格的第二个参数中的每个函数,在无限列表工作。上有限列表,具有该功能的非严格的第二个参数可以使它显着更有效(foldr (&&) True (False:replicate (10^9) True))。

你让争吵严格,如果你知道,在任何有价值的工作可以做反正参数必须进行评估。在许多情况下,GHC的严格性分析器可以自行完成,但当然不是。

一个非常典型的情况是在循环中或尾部递归,其中添加严格防止在途中巨大的thunk建设蓄电池。

我不知道哪里有严格的规定,对我来说这是一个经验问题,过了一段时间,你会学到在哪些地方增加严格度可能会有帮助以及伤害的地方。

作为一个经验法则,这是有道理的,以保持较小的数据(如Int)评价,但也有例外。

如何查看并检查我的程序中是否有空间泄漏?什么是构成空间泄漏的一般模式?

第一步是使用+RTS -s选项(如果程序与启用了rtsopts的链接)。这表明你整体使用了多少内存,并且你可以经常判断你是否有泄漏。 (另外,该程序需要与启用rtsopts被链接) 更翔实的输出可从与+RTS -hT选项,产生一个堆轮廓,可以帮助定位的空间泄漏运行的程序来获得。

如果需要进一步的分析,该程序需要启用分析编译(-rtsops -prof -fprof-auto,中老年GHCs的-fprof-auto选项不可用,则-prof-auto-all选项有最接近的对应)。

然后你运行它与各种分析选项,并查看生成的堆配置文件。

空间泄漏的两个最常见的原因是

  • 太懒惰
  • 太严格

第三位可能是采取不必要的共享,GHC做一点公共子表达式消除,但偶尔会在不需要的地方共享长列表。

为了找到泄漏的原因,我再次知道没有硬性规定,有时可以通过在一个地方添加严格性或在另一个地方添加懒惰来修复泄漏。

我该如何看看是否有太多懒惰的问题?我总是可以检查堆分析,但我想知道什么是懒惰伤害的一般原因,示例和模式?

一般来说,懒惰而被通缉,其中结果可以逐步建立起来的,和不想要在没有结果的一部分可以前处理完成,像留下折痕或一般尾递归函数传递。

+0

感谢一个非常好的答案。 – Satvik

3

我推荐阅读关于PragmasRewrite Rules的GHC文档,因为它们解决了许多关于SPECIALIZE和RULES的问题。

简单谈谈您的问题:

  • 专门用于强制编译器来构建一个多态函数的专用版本为特定类型。优点是在这种情况下应用该函数将不再需要字典。缺点是它会增加程序的大小。对于在“内部循环”中调用的函数来说,专门化特别有价值,对于不经常调用的顶级函数来说,它本质上是无用的。与INLINE交互请参考GHC documentation

  • RULES允许您指定重写规则,您知道这些规则是有效的,但编译器无法自行推断。常见的例子是{-# RULES "mapfusion" forall f g xs. map f (map g xs) = map (f.g) xs #-},它告诉GHC如何融合map。由于干扰INLINE,可能会让GHC使用规则。 7.19.3涉及如何避免冲突,以及如何强制GHC使用规则,即使它通常会避免冲突。

  • 在尾递归函数中,严格参数对像累加器之类的东西至关重要。你知道价值最终将被完全计算,并且建立一堆关闭来延迟计算完全失败了目的。强制严格必须自然地避免,只要该函数可以应用于必须被懒惰地处理的值,如无限列表。一般来说,最好的想法是最初只强制显示有用的地方(如累加器),然后仅在分析显示需要时才添加更多。

  • 我的经验是,大多数显示停止的空间泄漏来自懒惰的累加器和非常大的数据结构中的未评估的懒惰值,尽管我确定这是特定于您正在编写的程序类型。尽可能使用拆箱数据结构可以解决很多问题。

  • 实例之外那里懒惰导致空间泄漏,它应避免的主要情况是IO。懒惰地处理资源固有增加所需要的资源的的挂钟时间量。这可不好缓存性能,并且如果别的东西要专有权使用相同的资源这显然是不好的。