ByteStringでwcを書く
Haskellで速いwcを書いてみようという記事を読んだのですが、Haskellの文字列表現は他言語に比べ非常に効率が悪いわけで、それをつかってそこそこ速くしようとするとコードが汚くなってしまわざるを得ないという感じを受けました。
そこでByteStringで書いてみるとどうなるかという実験。
import qualified Data.ByteString as B import Data.ByteString.Base main = do text <- B.getContents -- lines putStr . show . length . B.split (c2w '\n') $ text putChar ' ' -- words putStr . show . length. B.splitWith isSpaceWord8 $ text putChar ' ' -- characters putStrLn . show . B.length $ text
textを三回使いまわし,別々に調べるという全くもって工夫の無いコードですが、Haskellで速いwcを書いてみようでも使っているFreeBSDのhandbook(3.8Mバイト)を入手して測定したところ
% ghc --make -O -fglasgow-exts wc.hs % time ./wc < book.txt 84709 654559 3848348 ./wc < book.txt 0.19s user 0.02s system 97% cpu 0.214 total
0.19sと十分速いです。メモリ使用量も1Mで,非常に少なかったです。
ちなみに-Oと-fglasgow-extsなしだと1.67sでした。
Haskellで効率の良いプログラムを書きたいときには
- ライブラリで用意されている関数を積極的に使う
- 例えば、ByteStringモジュールでは複数のループを一つのループにするループ融合という最適化などが行われているのですが、再帰を使って自前でループを書くとこういった最適化が阻害されてしまいます。(今回のプログラムではほとんど関係ないですが)
- データ構造を適切に選ぶ
- 大量のデータを扱う場合はByteStringやArrayなどの方が適している場合がある
- 最適化オプション-O -fglasgow-extsをつける。
あたりに注意するだけで,大分違ってくると思います。