« Japanize と Pathtraq が Firefox 3 Beta 1 に対応しました | メイン | ウェブアプリケーションにおけるHDDの正しい使い方 »

2007年12月28日

ディスクが1回転する間に複数回 fdatasync する方法について

 RDBMS のトランザクション速度は HDD の回転数に律速されるというのは、おそらく常識だと思います。たとえば MySQL のドキュメントには、以下のような記述を見ることができます。

もしディスクが OS を 「欺かなければ」、ディスクの回転速度は一般的に最大167 回転/秒で、コミット数も1秒につき167th に制限されます。

MySQL AB :: MySQL 5.1 リファレンスマニュアル :: 13.5.11 InnoDB パフォーマンス チューニング ヒント

 でもその限界って、ディスクの異なる角度の位置に複数のブロックを配置して、1周する間に順次 write + fdatasync していけば超えられるんじゃないか、ということで実証コードを書いてみました。

areasync.c
#include <fcntl.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>

#ifndef OFFSET
#  define OFFSET 128*1024
#endif
#ifndef NUM_BLOCKS
#  define NUM_BLOCKS 4
#endif

int main(int argc, char **argv)
{
  int fd;
  int i, j;
  
  assert((fd = open("tmp.0", O_CREAT | O_RDWR | O_TRUNC, 0666)) != -1);
  assert(lseek(fd, OFFSET * NUM_BLOCKS - 1, SEEK_SET) != -1);
  assert(write(fd, "", 1) == 1);
  
  for (i = 0; i < 100; i++) {
    for (j = 0; j < NUM_BLOCKS; j++) {
      assert(pwrite(fd, &i, sizeof(int), OFFSET * j) == sizeof(int));
      assert(fdatasync(fd) == 0);
    }
  }
  
  return 0;
}

 このコードを個人の開発環境 (DELL OptiPlex GX620; CentOS 5; HDT725050) で実行したところ、以下のような測定結果が得られました。

$ gcc -O3 -DOFFSET=65536 -DNUM_BLOCKS=1 areasync.c && for n in `seq 1 10` ; do /usr/bin/time -p ./a.out 2>&1 | grep real ; done
real 0.96
real 0.95
real 0.95
real 0.93
real 0.95
real 0.94
real 0.96
real 0.94
real 0.93
real 0.95
$ gcc -O3 -DOFFSET=65536 -DNUM_BLOCKS=2 areasync.c && for n in `seq 1 10` ; do /usr/bin/time -p ./a.out 2>&1 | grep real ; done
real 0.99
real 1.81
real 1.82
real 1.88
real 0.96
real 0.97
real 0.96
real 0.97
real 1.88
real 0.96

 上の -DNUM_BLOCKS=1 の方では、ファイルの同一位置を100回書き換えて、その度に fdatasync を行っています。使用している HDD の回転速度は 7200RPM ですから、ほぼ理論値に近い値が出ていると言えます。
 下の -DNUM_BLOCKS=2 の方では、65536 バイトの間隔をおいて配置された2カ所のデータを交互に書き換え、その度に fdatasync を呼び出しています。つまり、計200回 fdatasync しているのですが、注目すべきは、10回中6回において、上の場合とほぼ等しい所要時間が表示されているところです。これはつまり、1回転する間に2回 fdatasync に成功している、ということになります。
 一方、残り4回については、セクタの配置が連続しなかった等の理由で、1回転につき1回の fdatasync になったと考えられるでしょう。

 では、ブロック数を3以上に増やせば、さらに速度が上がるか、というところですが、これはうまくいかず、むしろ、ブロック数=2の場合が最速という結果が得られました。この現象については、うまい説明を考えることができません。なぜなんでしょう。また、この最適化手法が効果を発揮するかどうかは、環境に依存するようです (HDD やチップセット以外に linux のバージョンにも依存するかも)。興味をお持ちになった方は、自分の環境でお試しいただければと思います。また、本エントリに間違い等がありましたら、指摘いただければ幸いです。

 いずれにせよ、書き込み位置を複数用意することで、単位時間あたりのトランザクション回数を増やす、という最適化手法は有効なように思います。Q4M では、このあたりの成果も取り入れようかな、と思っています。

注: -DOFFSET=131072 -DNUM_BLOCKS=2 だと高速化できるので、トラックの容量が足りないわけではない

投稿者 kazuho : 2007年12月28日 13:27 このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク

トラックバック

このエントリーのトラックバックURL:
http://labs.cybozu.co.jp/cgi-bin/mt-admin/mt-tbp.cgi/1722