Sunday, October 30, 2016

mmapを使ってファイルベースの巨大なバッファを確保する話

小さなバッファはインメモリでもつが、メモリに収まらないような大きなバッファはテンポラリファイルを作り、file I/Oでアクセスする、というのが昔からの汎用的なバッファ実装のアプローチ。

だが、バッファに格納するデータ量によってアクセス手段を変えるというのはめんどくさいし、そこを抽象化すると無駄なオーバーヘッドが発生する。

幸いなことに最近は、メモリ空間が広い 64bit CPU だけ考えればいい。なので、ファイルの「読み込み」については、めんどくさいから全部mmapするというのが一般的なアプローチになってきている(例: LLVMのリンカであるlld)。

同様のことが、テンポラリファイルを使う可変長のバッファについても可能であり、h2o では実際に実装している。詳しくは h2o_buffer_reserve 関数の実装を見てもらえばいいと思いますが、ざっくりとした手順は以下のとおり:

▪️バッファ作成もしくはリサイズ処理:
  1. テンポラリファイルを作り、すぐ消す(作成の場合のみ。mkstemp, unlink してから file descriptor を使い続ける)
  2. サイズを設定する(posix_fallocate もしくは ftruncate)
  3. 旧来のマップされた領域を munmap する(リサイズの場合のみ)
  4. 全体を mmap する
▪️解放処理:
  1. munmap して close する

Windowsなら、ファイルを消すのを解放時にする必要があるかも。