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

标签 performance optimization haskell pragma

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

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

  • 是否有任何关于高级优化(在更高级别和非常低级别)的消息来源,特别是针对haskell?

    最佳答案

    Like when to use SPECIALIZE pragma and what performance gains it has.



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

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

    从 GHC-7 开始,给函数一个 {-# INLINABLE #-} 可能更有用。 pragma,使其(几乎不变,执行一些规范化和脱糖)源在接口(interface)文件中可用,因此该函数可以专门化,甚至可能在调用站点内联。

    Where to use RULES. I hear people taking about a particular rule not firing? How do we check that?



    您可以使用 -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 融合)是使用规则实现的。

    When to make arguments of a function strict and when does that help? I understand that making argument strict will make the arguments to be evaluated to normal form, then why should I not add strictness to all function arguments? How do I decide?



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

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

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

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

    一个非常典型的例子是循环或尾递归中的累加器,在这种情况下,增加严格性可以防止在途中产生巨大的 thunk。

    我不知道在何处添加严格性的硬性规定,对我而言,这是一个经验问题,一段时间后,您将了解在哪些地方添加严格性可能会有所帮助,而哪些地方会有害。

    根据经验,对小数据(如 Int)进行评估是有意义的,但也有异常(exception)。

    How do I see and check I have a space leak in my program? What are the general patterns which constitute to a space leak?



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

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

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

    空间泄漏的两个最常见原因是
  • 太懒了
  • 过于严格

  • 排在第三位的可能是不需要的共享,GHC 几乎不会消除常见的子表达式,但即使在不需要的地方,它偶尔也会共享长列表。

    对于查找泄漏的原因,我又没有一成不变的规则,偶尔,可以通过在一个地方添加严格或在另一个地方添加懒惰来修复泄漏。

    How do I see if there is a problem with too much lazyness? I can always check the heap profiling but I want to know what are the general cause, examples and patterns where lazyness hurts?



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

    关于performance - 何时使用各种语言编译指示和优化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12534145/

    相关文章:

    java - 如何自动从类数组转换为数组类

    python - bool 值的大列表和小列表以及字节数组的相对性能

    c# - 生成集合的排列(最有效)

    c - 优化代码以在 c 中获得以下输出

    list - Haskell 中后缀形式的中缀

    Haskell:打印函数内变量的类型

    haskell - 重叠模式匹配

    java - 在线性时间内找到未排序数组的中位数?

    C# 比 C++ 运行得更快?

    php - 存储用户 ID 列表的最佳方法