luo*_*990 12 performance haskell
到达错误分支时生成调用堆栈会花费运行时间;这很容易理解。
但是HasCallStack约束还会影响正常分支的性能吗?怎么样?
HasCallStack向函数添加约束的效果大致foo等同于:
foo的参数列表中;foo调用,都通过将信息框(由函数名“ foo”和被调用的源位置组成)压入输入调用栈(如果foo从具有HasCallStack约束的另一个函数中调用)来构造调用栈参数)或到一个空的调用栈中(如果从没有HasCallStack约束的函数中调用它)。所以...如果您有一些功能:
foo :: HasCallStack => Int -> String -> String
foo n = bar n '*'
bar :: HasCallStack => Int -> Char -> String -> String
bar n c str = if n >= 0 then c' ++ ' ':str ++ ' ':c'
else error "bad n"
where c' = replicate n c
baz :: String
baz = foo 3 "hello"
Run Code Online (Sandbox Code Playgroud)
然后添加HasCallStack到foo和bar(但不理会baz)基本上具有与您编写的效果相同的效果:
foo cs n = bar cs' n
where cs' = pushCallStack ("bar", <loc>) cs
bar cs n c str
= if n >= 0 then c' ++ ' ':str ++ ' ':c'
else error cs' "bad n"
where c' = replicate n c
cs' = pushCallStack ("error", <loc>) cs
baz = foo cs' 3 "hello"
where cs' = pushCallStack ("foo", <loc>) emptyCallStack
Run Code Online (Sandbox Code Playgroud)
因此,基准的,未优化的性能成本是每个装饰有功能的参数的额外成本,HasCallStack再加上为装饰后的功能的每个调用点提供该参数的重分配的成本。(即使未触发任何错误,也要支付这些费用。)
在实践中,优化后的代码将被……优化。例如,如果上面的示例使用编译-O2,foo将被内联并且bar将专门用于的定义,baz以使调用堆栈的唯一运行时开销是静态指针(指向用于创建完整调用堆栈的thunk) (用于error调用)传递给的专用版本bar(但会被忽略,因为不会产生错误)。
GHC似乎还不够聪明,无法确定这种baz情况永远不会发生error,因此根本不需要堆栈框架。
| 归档时间: |
|
| 查看次数: |
170 次 |
| 最近记录: |