Haskell中无限列表的编译器优化?

时间:2012-01-21 18:35:23

标签: performance optimization haskell ghc

我有t -> Maybe t类型的各种“部分置换”功能,可以通过返回Just将我带到数据结构中的新位置,或者如果他们返回Nothing还不能到那儿。

我经常必须在重复的特定模式中应用这些部分排列,构建所有中间值的列表,但每当我返回到起始位置或排列失败时截断列表。

scan_partial_perms :: Eq t => [t -> Maybe t] -> t -> [t]
scan_partial_perms ps v = map fromJust . takeWhile test $ scanl (>>=) (Just v) ps
   where  test (Just i) | i /= v = True
          test _ = False

iterate_partial_perm = scan_partial_perm . iterate
cycle_partial_perms = scan_partial_perms perms . cycle 

我非常确信scanl在此上下文中具有理想的严格性和尾递归属性。有关优化此代码的其他任何提示吗?特别是,我应该阅读-O3 -fllvm以外的哪些编译器选项?

在最坏的情况下,我可以使用定义为

的访问器函数替换scanl和无限列表
perm l i = l !! i `rem` length l

我认为这无法通过正确的优化来提高性能。

1 个答案:

答案 0 :(得分:2)

我认为你在scan_partial_perms中有一个错误,

scan_partial_perms ps v = map fromJust . takeWhile test $ scanl (>>=) (Just v) ps

scanl f s list始终以s开头,因此takeWhile test (scanl ...)[]。如果这是故意的,那就太混淆了。假设你想要的是

scan_partial_perms ps v = (v:) . map fromJust . takeWhile test . tail $ scanl (>>=) (Just v) ps

你无能为力。你可以{-# SPECIALISE #-},以便为专门的类型消除Eq字典。如果编译器本身不这样做(如果它可以看到使用站点,那么这对你有好处)。使用ghc> = 7,您可以将其设为{-# INLINABLE #-},以便它可以在每个使用网站上进行专门化并可能内联。

我不知道在llvm道路上会发生什么,但在核心级别,mapfromJusttakeWhile尚未内联,所以如果你绝望了如果以后没有在llvm后端内联它们,你可以通过手动内联它们得到几十分之一的百分之几:

scan_partial_perms ps v = v : go v ps
  where
    go w (q:qs) = case q w of
                    Just z
                      | z /= v -> z : go z qs
                    _ -> []
    go _ _      = []

但那些功能非常便宜,所以收益 - 如果存在的话 - 会很小。

所以你拥有的东西已经相当不错了,如果它不够好,你需要一个不同的攻击途径。

列表索引的那个,

perm l i = l !! (i `rem` length l)
-- parentheses necessary, I don't think (l !! i) `rem` length l was what you want

看起来不太好。 length价格昂贵,(!!)也很贵,所以一般都应该避免这两种情况。