マルチスレッドなら pwrite より mmap+msync のが速い (or Q4M のパフォーマンスの限界値に到達) という話

色々ユーザランドでのロックを使わない形に書き換えても、なかなか速くならなかったのが、pwrite をやめて mmap(PROT_WRITE) 経由での書き込みにしたら、20% 程度あった idle time が 0 になった。

ベンチマーク的にはこんな感じ。上が pwrite 経由。下が msync(MS_ASYNC) 経由の書き込み。どちらも読み込みは mmap

$ USE_C_CLIENT=1 MESSAGES=400000 CONCURRENCY=40 DBI='dbi:mysql:test;mysql_socket=/tmp/mysql51.sock;user=root' t/05-multireader.t 
1..4
ok 1 - check number of messages
ok 2 - min value of received message
ok 3 - max value of received message
ok 4 - should have no rows in table


Multireader benchmark result:
    Number of messages: 400000
    Number of readers:  40
    Elapsed:            13.326 seconds
    Throughput:         30016.656 mess./sec.


$ USE_C_CLIENT=1 MESSAGES=400000 CONCURRENCY=40 DBI='dbi:mysql:test;mysql_socket=/tmp/mysql51.sock;user=root' t/05-multireader.t 
1..4
ok 1 - check number of messages
ok 2 - min value of received message
ok 3 - max value of received message
ok 4 - should have no rows in table


Multireader benchmark result:
    Number of messages: 400000
    Number of readers:  40
    Elapsed:            10.744 seconds
    Throughput:         37229.444 mess./sec.

てか、pwrite は、API 的にはステートへの依存がなくなっているけど、(少なくとも手元の linux 環境 (x86_64; 2.6.18-53.1.14.el5; ext3) では) 内部での並走性はない (or 小さい) ってことですね。もっと早くここをいじるべきだったorz ... でもまあ他の lock も減らせたしいいことにしよう。単純な SELECT ... LIMIT 1 クエリの実行可能回数が 75k回/秒だったから、1行消費するために2回クエリを発行する Q4M の場合、パフォーマンスの限界値に到達したって感じw

ちなみに mmap+msync 経由の書き込みを使いたい場合は、Q4M の ./configure に --enable-mmap-writes=yes としてください。