Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Optimization 引导循环不变码运动出GHC_Optimization_Haskell - Fatal编程技术网

Optimization 引导循环不变码运动出GHC

Optimization 引导循环不变码运动出GHC,optimization,haskell,Optimization,Haskell,我一直在努力解决GHC中的低级手动循环优化问题。我的程序包含一些执行数值计算的循环。真实数据被包装在其他数据结构中,程序被分解为“循环控制流函数”和“计算函数”,这样一些数据结构字段最终会在内部循环中读取。我希望GHC将这些读数移出内部循环。下面是代码的简化版本,以显示发生了什么 data D = D !Double !C data C = C Double -- This function is called in every loop iteration. -- Parameter 'c'

我一直在努力解决GHC中的低级手动循环优化问题。我的程序包含一些执行数值计算的循环。真实数据被包装在其他数据结构中,程序被分解为“循环控制流函数”和“计算函数”,这样一些数据结构字段最终会在内部循环中读取。我希望GHC将这些读数移出内部循环。下面是代码的简化版本,以显示发生了什么

data D = D !Double !C
data C = C Double

-- This function is called in every loop iteration.
-- Parameter 'c' is loop-invariant.
exampleLoopBody i a c =
  case c of C b -> a + b * fromIntegral i

-- The body of this function is a counted loop that should be optimized
foo x =
  case x
  of D acc0 c ->
    let loop i acc =
          if i > 100
          then acc
          else loop (i+1) (exampleLoopBody i acc c)
    in loop 0 acc0
每个循环迭代都计算cb的情况c,但这是冗余计算,因为c是循环不变的。我可以通过在循环外放置一个冗余的大小写表达式,使GHC将其取出:

foo x =
  case x
  of D acc0 c ->
    case c             -- This case statement inserted for optimization purposes
    of C b -> b `seq`  -- It will read 'b' outside of the loop
      let loop i acc =
           if i > 100
           then acc
           else loop (i+1) (exampleLoopBody i acc c)
      in loop 0 acc0
编译器内联线
exampleLoopBody
。之后,内部case语句是多余的,并被消除:

foo x =
  case x
  of D acc0 c ->
    case c
    of C b -> b `seq`
      let loop i acc =
            if i > 100
            then acc
            else loop (i+1) (acc + b * fromIntegral i) -- The inlined case expression disappears
      in loop 0 acc0
seq
的目的是确保大小写表达式不是死代码。
seq
检查
b
是否为
|
。GHC注意到,由于已计算了
b
,因此在循环体中重用该值很有用

现在,问题来了:我真的希望所有相关的数据字段都要严格。如果我在数据定义中插入严格性注释,如下所示

data C = C !Double
那么,就GHC而言,
seq
案例c/b
没有任何影响。GHC删除了它们,我得到:

foo x =
  case x
  of D acc0 c ->
    let loop i acc =
          if i > 100
          then acc
          else loop (i+1) (case c of C b -> acc + b * fromIntegral i) -- Evaluate the case in every iteration
     in loop 0 acc0
这段代码在每次迭代中都对cb的案例c求值,这正是我试图避免的


如果我不能依赖于
seq
,我不知道如何强制在循环体之外计算
b
。在这种情况下,我可以使用一些技巧吗?

您可以尝试重新排列参数并将循环变量部分移动到lambda中:

-- note the order of the arguments changed
exampleLoopBody (C b) =
  \i a -> a + b * fromIntegral i

foo (D acc0 c) =
    let
       loopBody = exampleLoopBody c 
       loop i acc =
          if i > 100
         then acc
         else loop (i+1) (loopBody i acc)
   in loop 0 acc0

此外,这段代码此时建立了一个大的未赋值表达式,因此您可能希望每次通过循环时强制使用累加器参数。

这看起来基本上是将整个原因
newtype
放在该语言中的。只需更改
数据C=C!Double
newtype C=C Double
并编写代码的原始版本。类型为
C
的值上的所有
case
表达式都将被删除。请注意,示例中的代码模式:

case foo of
    D acc0 c -> case c of
        C b -> ...
可以更简洁地写:

case foo of
    D acc0 (C b) -> ...

您正在使用哪个GHC版本?在7.4.1和7.2.2的每次迭代中,我都得到了不错的内核,没有
case
。纯未装箱的
Double#
s.@DanielFischer我使用的是7.0.3,其中一个
Double
s在经过简化程序后装箱。仅供参考,我的实际用例实际上涉及静态大小的向量,例如
Cons-Double(Cons-Double-Nil)
。我可以试着在一个更新的GHC中运行它,看看会发生什么。嗯,我也得到了一个7.0.4版本的未绑定循环。
$wfoo
worker有一个装箱参数(也有7.4.1和7.2.2),但是循环本身是
letrec
在里面,并接受一个未装箱的
Double
(另一个
Double
是静态的,偶数)。是的,
$wfoo
有一个装箱参数,循环获取并返回unbox
Double#
s。
$wfoo
的装箱参数由循环体内部的
大小写
解除装箱。对我来说,
大小写
在循环体外部。它会是32位还是64位?你的操作系统是什么?我问这个问题时可能把问题简化得太多了。在实际程序中,
foo
是一个多态函数。
c
的类型各不相同,可能有多个字段。