椿の日記

たぶんプログラムの話をします

Haskellの実行速度

haskell vs その他の言語 - ながとの日記 という記事を見て、へぇーと思ってみてたのですが、元記事のコードの次の2つが何故これほどまでに違うのか、ちょっと気になったのでコンパイルだけして実際に確認してみます。(2つ目のはBangPatternの言語拡張のとこだけ削除)

import Control.Monad
import qualified Data.HashTable as H

main = do
    m <- H.new (==) H.hashInt
    forM_ [1..10000000] $ \n -> H.insert m n n
    v <- H.lookup m 100
    print v
import qualified Data.HashTable as H
import Data.Maybe

eqInt :: Int -> Int -> Bool
eqInt a b = a == b

type HT = H.HashTable Int Int

n = 1000000

loadHashtable :: HT -> IO ()
loadHashtable ht = work n
  where
    work :: Int -> IO ()
    work i = do
      if i < 0 then
          return ()
        else do
          H.insert ht i i
          work (i-1)

main = do
  h <- H.new eqInt H.hashInt
  loadHashtable h
  i <- (H.lookup h 100) >>= (return . show . fromJust)
  putStrLn i

確かに後者のほうが圧倒的に速いです。恐らく違いは数字の列挙の方法にあり、前者は巨大なリストの走査で、後者はループカウンタの利用、ということなのでしょうか。
ただ現実問題として毎回後者のように書くのもアレなので、アクション部分を入れ替えられる高階関数を用意したほうが良さそうですね。

forIntM_ :: Int -> (Int -> IO ()) -> IO ()
forIntM_ n act = work n
  where
    work :: Int -> IO ()
    work i = do
      if i < 0 then
        return ()
      else do
        act i
        work (i-1)

loadHashtable :: HT -> IO ()
loadHashtable ht = forIntM_ n (\i -> H.insert ht i i)