GHC中模式匹配的表现

时间:2017-01-19 07:20:02

标签: haskell pattern-matching ghc

我正在为我创建的数据类型编写一个“附加”函数(基本上处理“流”)。但是,这个数据类型有12个不同的构造函数,处理不同类型的“流”,例如,无限,空,固定长度,可变长度,已经附加等。

输入类型和输出类型之间的逻辑有点复杂但不是那么令人难以置信。

我考虑了两种方法:

  1. 与广泛的类别匹配(可能通过包装在更简单的代理类型中),然后在这些匹配内匹配OR
  2. 只对144个案例(12 * 12)进行模式匹配。对于特定的组合,我可以通过通配符匹配将此值减少到100,但这就是它。
  3. 我知道第二种方法更难以维护,但忽视这一点,GHC会发现第二种方法更容易优化吗?如果它可以使用简单的跳转表(或者可能是两个跳转表)进行第二种方法,我怀疑它会更快。但是,如果它正在进行线性检查,那将会慢得多。

    GHC是否将模式匹配(甚至非常大的匹配)优化为恒定时间跳转表?

2 个答案:

答案 0 :(得分:6)

是的,GHC优化了这种模式匹配。前七个(我认为)构造函数通过指针标记得到优化。我相信其余的将由跳桌来处理。但144个案例听起来很难维护,你必须注意代码大小。你真的需要所有这些案件吗?

答案 1 :(得分:2)

编写一个小的Haskell脚本编写一个巨大的case-block和一个小的基准测试并不难。例如:

module Main (main) where

mapping = zip ['!'..'z'] (reverse ['!'..'z'])

test_code =
  [
    "module Main where",
    "",
    "tester :: String -> String",
    "tester cs = do",
    "  c <- cs",
    "  case transform c of",
    "    Just c' -> [c']",
    "    Nothing -> [c ]",
    "",
    "input = concat [ [' '..'z'] | x <- [1..10000] ]",
    "",
    "main = print $ length $ tester $ input",
    ""
  ]

code1 =
  test_code ++
  [
    "transform :: Char -> Maybe Char",
    "transform c = lookup c " ++ show mapping
  ]

code2 =
  test_code ++
  [
    "transform :: Char -> Maybe Char",
    "transform c =",
    "  case c of"
  ] ++
  map (\(k, v) -> "    " ++ show k ++ " -> Just " ++ show v) mapping ++
  [
    "    _ -> Nothing"
  ]

main = do
  writeFile "Test1.hs" (unlines code1)
  writeFile "Test2.hs" (unlines code2)

如果您运行此代码,它会生成两个小的Haskell源文件:Test1.hsTest2.hs。前者使用Prelude.lookup将字符映射到字符。后者使用了一个巨大的案例块。这两个文件都包含将映射应用于大型数据列表并打印出结果大小的代码。 (这样可以避免I / O,否则I / O将成为主导因素。)在我的系统上,Test1需要几秒钟才能运行,而Test2几乎是即时的。

过度感兴趣的读者可能会尝试将其扩展为使用Data.Map.lookup并比较速度。

这证明模式匹配比键/值映射列表的O(n)遍历要快得多......这不是你提出的问题。但随意制定自己的基准测试。您可以尝试自动生成嵌套案例与平面案例并对结果进行计时。我猜测是你不会发现很多不同之处,但可以随意尝试。