【问题标题】:GHC Performance: Why does more work take *much* less time?GHC 性能:为什么更多的工作需要*多*少的时间?
【发布时间】:2013-02-10 19:29:08
【问题描述】:

不幸的是,整个示例涉及大量代码。你可以看到完整的模块here(仍然不会编译),下面的伪代码函数f对应于hpaste中的'FIXME'标签。

这是一个伪代码大纲:

module Test (run) where
    import Data.Vector.Unboxed as U

    run m i iters = let {get q} in do print $ testWrapper iters m q

    testWrapper :: forall i . Int -> Int -> i -> U.Vector i
    testWrapper iters m q =
        let {get test params: xs, dim, ru}
        in U.map fromIntegral (iterate (f dim ru) xs !! iters)

    {-# INLINE f #-}
    f :: (Int, Int) -> Vector r -> Vector r -> Vector r
    f dim ru = (g dim ru) . zipWith (*) ru

    {-# INLINE g #-}
    g :: (Int, Int) -> Vector r -> Vector r -> Vector r
    g dim ru = ...

对于某些参数,此代码在 ~.5 秒内运行。

我还测试了将 f 更改为 f':

f' dim ru = (g dim ru)

(我只是删除了最终的 zipWith,减少了所需的整体工作量)。

在相同的输入参数上,修改后的代码需要 4.5 秒。

在使用 optimizaiton 进行编译时会发生这种情况(使用 GHC 7.4.2、ghc -O2 以及更多优化)。快版的内核大约3000行,慢版的内核大约1900行。

这可能没什么好做的,但是什么样的 GHC 疯狂可能会导致我的程序通过减少它所做的工作而减速一个数量级? 当我最小的测试用例基本上生成超过 2000 行核心时,怎么会发现这样的东西?

谢谢

【问题讨论】:

  • 我怀疑慢版本会重新计算多态 ru,而快版本只计算一次。
  • 我该如何解决这个问题,为什么会这样?

标签: performance haskell ghc


【解决方案1】:

查看堆配置文件。会不会是“更少的工作”版本留下了一些未评估的 thunk?这可能会导致大量内存占用,并通过垃圾收集影响速度。

【讨论】:

  • 我只能想象thunk在“ru”中,它是一个向量。我怎样才能避免这种重击?堆分析器顶部有“basicUnsafeNew/ru ...”(具有陡峭的负斜率),这似乎也指向 ru。我添加了几个 'seq's (so f' dim ru = ru seq (g dim ru)),但没有效果。
  • sstderr 结果的另一条注释:快速版本在堆上分配 1.5GB,慢速版本分配 10.5GB。快速版本中的“Gen 0”行显示“2788 colls”,而慢速版本中的“Gen 0”显示“20288 colls”。慢速的 MUT 是 4.64 秒,而快速的 MUT 是 0.65 秒(这些区域也基本上是各自的运行时间)。分配率和 GC 时间是可比的。我看到有 一个问题,但我不知道如何解决它。感谢您的任何提示!
猜你喜欢
  • 2015-01-20
  • 2010-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-10
  • 1970-01-01
  • 2017-01-28
相关资源
最近更新 更多