【问题标题】:When to use various language pragmas and optimisations?何时使用各种语言编译指示和优化?
【发布时间】:2012-09-14 02:09:07
【问题描述】:

我对 haskell 有一些了解,但我总是不确定应该使用什么样的编译指示和优化以及在哪里使用。喜欢

  • 比如什么时候使用SPECIALIZE pragma 以及它有什么性能提升。
  • 在哪里使用RULES。我听说人们正在考虑不触发的特定规则?我们如何检查?
  • 什么时候使函数的参数严格,什么时候有帮助?我知道使参数严格会使参数被评估为正常形式,那么为什么我不应该对所有函数参数添加严格性呢?我该如何决定?
  • 如何查看和检查我的程序中是否存在空间泄漏?构成空间泄漏的一般模式是什么?
  • 如何查看是否存在懒惰过多的问题?我总是可以检查堆分析,但我想知道导致懒惰的一般原因、示例和模式是什么?

是否有任何关于高级优化(包括高级优化和非常低级)的资源,特别是针对 haskell 的?

【问题讨论】:

  • @DonStewart 谢谢.. 我已经阅读了 RWH.. 问题是我知道他们在做什么,但我不知道他们的使用时间和地点是重要的还是不重要的。我问这个问题是为了找出其他来源以进一步加深我的理解。

标签: performance optimization haskell pragma


【解决方案1】:

比如什么时候使用SPECIALIZE pragma 以及它有什么性能提升。

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

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

从 GHC-7 开始,给函数一个 {-# INLINABLE #-} pragma 可能更有用,这使得它的(几乎没有变化,执行了一些规范化和去糖)源在接口文件中可用,所以函数可以被专门化甚至可能在调用站点内联。

在哪里使用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 fusion)是使用规则实现。

什么时候使函数的参数严格,什么时候有帮助?我知道使参数严格会使参数被评估为正常形式,那么为什么我不应该对所有函数参数添加严格性呢?我该如何决定?

使参数严格将确保它被评估为弱头范式,而不是范式。

您不必使所有参数都严格,因为某些函数的某些参数必须是非严格的才能工作,而有些函数如果对所有参数都严格则效率较低。

对于example partition 在其第二个参数中必须是非严格的,才能在无限列表上工作,更一般地说,foldr 中使用的每个函数在第二个参数中必须是非严格的,才能在无限列表上工作.在有限列表上,在第二个参数中使用 non-strict 函数可以显着提高效率 (foldr (&&) True (False:replicate (10^9) True))。

如果您知道必须先对参数进行评估,然后才能完成任何有价值的工作,那么您就可以使参数严格。在很多情况下,GHC 的严格度分析器可以自行完成,但当然不是全部。

一个非常典型的情况是循环或尾递归中的累加器,在这种情况下,增加严格性会阻止在途中构建巨大的 thunk。

我不知道在哪里增加严格性的硬性规定,对我来说这是经验问题,一段时间后你就会知道在哪些地方增加严格性可能有帮助,在哪里有害。

根据经验,评估小数据(如Int)是有意义的,但也有例外。

如何查看和检查我的程序中是否存在空间泄漏?构成空间泄漏的一般模式有哪些?

第一步是使用+RTS -s 选项(如果程序链接时启用了rtsopts)。这向您显示了总体使用了多少内存,您通常可以判断您是否有泄漏。 使用+RTS -hT 选项运行程序可以获得更多信息输出,该选项会生成有助于定位空间泄漏的堆配置文件(此外,该程序需要与启用的 rtsopts 链接)。

如果需要进一步分析,则需要在启用分析的情况下编译程序(-rtsops -prof -fprof-auto,在较旧的 GHC 中,-fprof-auto 选项不可用,-prof-auto-all 选项是那里最接近的对应关系)。

然后您使用各种分析选项运行它并查看生成的堆配置文件。

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

  • 太懒了
  • 过于严格

第三位可能是不受欢迎的共享,GHC 很少消除常见的子表达式,但它偶尔会共享长列表,即使在不需要的地方。

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

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

通常,在可以逐步建立结果的情况下需要惰性,而在处理完成之前无法交付结果的任何部分的情况下不需要惰性,例如在左折叠或通常在尾递归函数中。

【讨论】:

  • 感谢您提供非常好的答案。
【解决方案2】:

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

简要回答您的问题:

  • SPECIALIZE 用于强制编译器为特定类型构建多态函数的专用版本。优点是在这种情况下应用该函数将不再需要字典。缺点是会增加程序的大小。专业化对于在“内部循环”中调用的函数特别有价值,而对于不经常调用的顶级函数来说,它基本上是无用的。有关与 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 使用规则,即使它通常会避免它。

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

  • 1234563尽可能使用未装箱的数据结构可以解决很多问题。
  • 在惰性导致空间泄漏的情况之外,应该避免的主要情况是在 IO 中。延迟处理资源本质上会增加所需资源的挂钟时间量。这可能不利于缓存性能,如果其他人想要独占权使用相同的资源,这显然是不好的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-11
    • 1970-01-01
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    • 2013-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多