2015-12-07
[Haskell]僕が人生で起こした唯一のスペースリーク
これは、Haskellスペースリーク Advent Calendar 2015の7日目の記事です。
僕は Haskell で主に IO なコードを書いているからか、あまりスペースリークを起こしたことがない。これまで起こした唯一のスペースリークは、サーバプログラムの中の以下のようなコードだった。
atomicModifyIORef ref (\_ -> (tmstr, ()))
これは、時間を文字列に変換して IORef にキャッシュさせるコードだ。あるスレッドが、毎秒このコードを呼び出す。不思議なことに、クライアントからのアクセスがあるとスペースリークにはならないのだが、長時間アクセスがないとサーバのプロセスが太っていた。
このリークは、結果を利用していないために起こる。そもそも、このコードは古い値を利用せずに、単に新しい値で置き換えているだけだから、writeIORef で十分だ。
writeIORef ref tmstr
では、カウンターのように古い値を使う場合はどうすればいいだろう?
atomicModifyIORef ref (\i -> (i+1, ()))
このスペースリークをなくすための定石は、結果を評価してやることである。
x <- atomicModifyIORef ref (\i -> (i+1, ())) x `seq` return ()
BanPatterns を使うと、すこしスッキリする。
!_ <- atomicModifyIORef ref (\i -> (i+1, ()))
一番よいのは、CAS で値を置き換えるときは遅延評価で、CAS が成功し後に正格評価する atomicModifyIORef' を使うことだ。
atomicModifyIORef' ref (\i -> (i+1, ()))
おまけ
昔この話を Monad.Reader 19に書いたところ、atomicModifyIORef のマニュアルに注意書きが足された。:-)
atomicModifyIORef' は、並行 Haskell の奥義である。詳しくは「Haskellによる並列・並行プログラミング」を読んでほしい。
トラックバック - http://d.hatena.ne.jp/kazu-yamamoto/20151207/1449539746
リンク元
- 35 https://t.co/aa1M2Mko99
- 14 https://www.google.co.jp/
- 11 http://qiita.com/advent-calendar/2015/haskell-space-leaks
- 10 https://t.co/hYBHG711Iz
- 8 https://www.google.co.jp
- 5 http://feedly.com/i/latest
- 4 http://b.hatena.ne.jp/
- 4 http://qiita.com/
- 3 http://qiita.com/ruicc/items/bfa659c2ef9e1f75f7e1
- 3 http://www.google.co.uk/url?sa=t&source=web&cd=1