何时使用各种语言编译指示和优化?

Sat*_*vik 15 optimization performance haskell pragma

我对haskell有一点了解,但我总是不确定我应该使用什么样的pragma和优化以及在哪里.喜欢

  • 就像什么时候使用SPECIALIZEpragma以及它有什么性能提升一样.
  • 在哪里使用RULES.我听说人们采取了一个不解雇的特定规则?我们如何检查?
  • 什么时候使函数的参数严格,什么时候有帮助?我理解,使参数严格将使参数被评估为正常形式,那么为什么我不应该对所有函数参数添加严格性?我该如何决定?
  • 我如何查看并检查程序中是否有空格泄漏?造成空间泄漏的一般模式是什么?
  • 我如何看待是否有太多懒惰的问题?我总是可以检查堆分析,但我想知道什么是懒惰伤害的一般原因,示例和模式?

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

Dan*_*her 18

就像什么时候使用SPECIALIZEpragma以及它有什么性能提升一样.

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

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

从GHC-7开始,为函数提供一个{-# INLINABLE #-}pragma 可能更有用,它使得它(在接口文件中几乎没有变化,一些规范化和去除化)源可用,因此该函数可以是专用的,甚至可以在呼叫网站.

在哪里使用RULES.我听说人们采取了一个不解雇的特定规则?我们如何检查?

您可以使用-ddump-rule-firings命令行选项检查已触发的规则.这通常会转储大量已解雇的规则,因此您必须搜索一下您自己的规则.

你使用规则

  • 当你有一个更有效的特殊类型的函数版本,例如

    {-# RULES
    "realToFrac/Float->Double"  realToFrac   = float2Double
      #-}
    
    Run Code Online (Sandbox Code Playgroud)
  • 当某些函数可以替换为特殊参数的更高效版本时,例如

    {-# 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
      #-}
    
    Run Code Online (Sandbox Code Playgroud)
  • 当根据一般规律重写表达式时,可能会产生更好的优化代码,例如

    {-# RULES
    "map/map"  forall f g. (map f) . (map g) = map (f . g)
      #-}
    
    Run Code Online (Sandbox Code Playgroud)

RULES后一种风格的广泛使用在融合框架中进行,例如在text库中,并且对于列表函数base,foldr/build使用规则实现不同类型的融合(融合).

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

使参数严格将确保它被评估为弱头正常形式,而不是正常形式.

你没有使所有参数都严格,因为有些函数在它们的一些参数中必须是非严格的,而且如果在所有参数中都是严格的则一些函数效率较低.

对于例如 partition必须是非严格在其第二个参数上无限列表在所有的工作,更普遍的应用在每一个功能foldr必须是非严格的第二个参数上无限列表工作.在有限列表中,在第二个参数中使用非严格函数可以使其显着提高效率(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几乎没有消除共同的子表达式,但它偶尔会在不需要的地方共享长列表.

为了找到泄漏的原因,我再次知道没有严格的规则,偶尔可以通过在一个地方添加严格性或在另一个地方添加懒惰来解决泄漏问题.

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

通常,需要懒惰,其中结果可以递增地构建,并且在处理完成之前不能传递结果的任何部分,例如在左侧折叠中或通常在尾部递归函数中.