用事の無い急な休みはいらない!

暇だよ、暇。

急な休みだから空いてる友だちもいないし。とりあえず、Red Hat Linuxのbacklogについて実験してみた。backlogってのはBSDソケットでサーバ側がlisten呼ぶときの二つ目の引数のこと。

#include 
int listen(int s, int backlog);

man listenからbacklog関係の部分を引用しておく

backlog 引き数は、保留中の接続のキューが拡張することのできる最大長を指定する。

(中略)

注意
TCP ソケットでの backlog 引数の振る舞いは Linux 2.2 で変更された。現 在ではこの引数は、受け付けられるのを待っている、 完全に確立されたソケットのキューの長さを指定する。以前は不完全な接続要求の数であったが、これを置き換えた。

ここでいう不完全な接続要求というのは多分3-way-ハンドシェークのクライアントからのACK待ち状態の事だと思う(これをSYN_RCVDという)。で、確立されたソケットってのは3-way-ハンドシェークが完了したソケット(これをESTABLISHEDという)のことでしょう。

backlogの最大値はSOMAXCONNで分かる。俺の環境では128だった。一般的には(というかBSD的には)5に制限されているところを見ると十分過ぎる程の大きさだ。で、実際にいくつの接続がキューされるか調べたところ、backlogに3足して、131よりも大きいときは131にカットされ、131より小さいときはbacklog+3が用いられているようだ。

だからbacklogに0を指定しても3つはキューされる(これに頼るのはだめ)。

manから察するにlistenに与えるbacklogはESTABLISHEDな接続のキューなので、暗黙的に追加される3というのが(意味的には)SYN_RCVDな接続の為のスペースなのかな。

またベンチマーク

ベンチマーク厨とでも呼んで下さい。

参照:http://proglang-0.ten.thebbs.jp/1079174593/

このスレッドでちょっと興味を持ったので、char配列の初期化について調べてみた。このスレッドに書いたこととも重複するけど、まとめの意味です。Cで文字列を初期化(空文字列)にするときは以下のような方法が考えられます。

(1) char str[1024] = {0};
(2) char str[1024] = "";
(3) char str[1024]; str[0] = '\0';

はじめに言っておきますが、ダントツで(3)が最速です。というか、そもそも意味が違います。(1),(2)はそれぞれstr配列の領域すべてを0で初期化しますが、(3)は最初の要素のみ初期化します(文字列的にはこれでOK)。

1,000,000回上記3通りの初期化をループしたところ結果は

(1) 1.14 sec
(2) 0.43 sec
(3) 0.01 sec

コンパイルは[gcc -O2 benchmark.c]としました。

となりました。もはや結果はどうでもよいです。文字列の初期化ってあんまり使わないし(大抵は明示的にstrncpyやsnprintfなどで具体的な文字を代入してしまうので、初期化は必要無い)、必要があるなら(3)でOKでしょう。見た目がうざったくに見えるなら

#define initstr(s) s[0] = '\0'
char str[1024];
initstr(str);

とすれば良いでしょう。むしろ気になったのはなんで(1)があんなに遅いのかってこと。アセンブラに落してみたらこうなりました(一部抜粋)

    subl    $1036, %esp
    pushl   $1023
    movb    .LC0, %al
    movb    %al, -1032(%ebp)
    pushl   $0
    leal    -1031(%ebp), %eax
    pushl   %eax
    call    memset

おぉい、なんでmemset呼んでるんだい? アセンブリ言語ってあんまり流暢じゃないんだけど、もっと上手いやり方ありそうだけどなぁ。ちなみに(2)はこうなる。

    cld
    pushl   %edi
    movl    $256, %ecx
    leal    -1032(%ebp), %edi
    xorl    %eax, %eax
    subl    $1028, %esp
    rep
    stosl
    movl    -4(%ebp), %edi

やっぱ、奇麗だね。(1)の方をrep stoslで置き換えてみたけど、あんまり速くならなかった。もしかして、memsetが問題なんじゃなくて、movb .LC0, %alのほうが問題。ともかく餅は餅屋で、配列の初期化は配列の初期化用の構文使うのが良い、と。