サイズによるmalloc(3)の負荷の違い
ネットワークIOのバッファリングを行うコードを書く必要があったので、確保するメモリのサイズによってmalloc(3)にかかる時間がどれくらい違うのか調べてみた。
環境は:
Linux vcore.local 2.6.22.9-vcore16 #1 SMP Sun Oct 14 22:13:32 JST 2007 x86_64 AMD Athlon(tm) 64 X2 Dual Core Processor 5000+ GNU/Linux
malloc() → free() を 1,000,000回繰り返し、かかった時間を計測した結果:
$ ./a.out 8 16 32 64 128 256 512 1024 8192 16384 32768 bytes: seconds 8: 0.042706 16: 0.042608 32: 0.042998 64: 0.042607 128: 0.093424 256: 0.093280 512: 0.092128 1024: 0.092512 8192: 0.089559 16384: 0.091815 32768: 0.091766
64バイトまでと、128バイト以降では倍以上の差がある。
malloc(3)のメモリ管理構造 VA Linux Systems Japan を読むと、72バイトまでのチャンクは「fastbins」に登録される、「fastbinsは要求メモリを迅速に確保する為に用意されたchunkのリストヘッダ」とある。
そこで72バイト周辺をもう一度調べてみると、確かに72バイトを境界に速度に大きな差が出る。
$ ./a.out 71 72 73 74 bytes: seconds 71: 0.042752 72: 0.042665 73: 0.092959 74: 0.093006
72バイトを越えると32KBまでほとんど変わらないので、バッファリング用には一定のサイズ決め打ちでメモリを確保すれば良さそう。
クライアントの数だけバッファリング用のメモリを保持しなければならないが、Linuxは楽観的にメモリを確保するので、確保するだけではメモリを消費したことにならないから問題ないはず。
他のOSで単純に大きなバッファをバンバン確保すると、クライアントの数が増えたときにメモリを確保できなくなるかもしれない。Linuxでもovercommitに頼りすぎるといざという時にOOM Killerが走り出して良くないので、C10Kな規模のクライアントが接続してくる場合は別なバッファリング戦略が必要かもしれない。
テストに使ったコード:
#include <stdio.h> #include <stdlib.h> #include <sys/time.h> static double g_timer; double gettimeofday_sec() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec + (double)tv.tv_usec * 1e-6; } void timer_init() { g_timer = gettimeofday_sec(); } void timer_show() { printf("%f\n", gettimeofday_sec() - g_timer); } #define NUM_LOOP 1000000 int main(int argc, char* argv[]) { int i, j; printf(" bytes: seconds\n"); for(i=1; i < argc; ++i) { size_t sz = atoi(argv[i]); printf("%8lu: ",sz); timer_init(); for(j=0; j < NUM_LOOP; ++j) { char* buf = (char*)malloc(sz); free(buf); } timer_show(); } return 0; }