LoginSignup
13
14

More than 5 years have passed since last update.

シーケンシャルリードのブロックサイズはどのくらいが適切か

Posted at

ファイルの内容をネットワークに書く場合、頑張ってファイル読み込みとネットワークへの書き込みを並行して動かすのは面倒なので、普通は次のようなコードになると思います。

while True:
    data = f.read(BLKSIZE)
    if not data:
        break
    sock.sendall(data)

さて、この時の BLKSIZE はどれくらいの大きさが適切でしょうか?

とりあえず思いつくのは4KB (x86 のデフォルトのページサイズ) ですが、ネットワークやディスクが十分に高速な場合、ちょっとシステムコールが多すぎになる気がします。 Python みたいな遅いプログラミング言語を使ってる場合はループに含まれるメソッド呼び出しのオーバーヘッドなんかも無視できないので、もうちょっと大きくしたい気がします。
富豪的に、一気に1MBとかにしてみたい気もしますが、ちょっと待ってください。このプログラムはシーケンシャルに見えますが、ブロックサイズが小さい間はディスクアクセスとネットワークアクセスが並行して動いています。

まず、ファイルからの読み込みは readahead という仕組みで、最大128KBくらいまでは先読みが効きます。
ネットワーク側がボトルネックになった場合、ブロックサイズを128KB未満にしておくことで、ディスクからの読み込みでブロックする時間をほぼゼロにして、ネットワークのスループットを無駄にせずに済みます。

一方、ソケットへの書き込みも同じくOSのソケットバッファに対して行われます。例えば TCP の送信バッファであれば、 cat /proc/sys/net/ipv4/tcp_wmem で最小値、デフォルト値、最大値を知ることができます。私の環境 (Ubuntu willy で sysctl などを変更していない) では 4096 16384 4194304 でした。デフォルトが16KBで、OSが状況に応じて最大で4MBまで増やす可能性があるということです。

ソケット以外の書き込み先として、パイプの場合だと、一般的なバッファサイズは64KBのようです。 (参考)

ユーザーランドのバッファを、カーネル内のバッファからバッファへデータをコピーするためのバケツだと考えた場合、バケツがカーネル内のバッファより大きいとスムースにコピーができません。
ブロックサイズを決めるときは、下限を4KB、上限はパイプのことを考慮に入れる必要があるなら64KB、無いなら128KBを上限として、その間のどこかを適当に選ぶのが良いと思います。

Note
もちろんこれは、一般的な環境でそこそこ効率よくプログラムを書きたいとか、設定可能なサイズのデフォルト値を決めたいとかいった場合に参考になる基準であって、特定の環境で最大限の性能を出すためのチューニングをする場合は話は別ですよ。

13
14
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
14