SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

ファイルディスクリプタについて

ファイルディスクリプタについて(6)
~多重I/Oの性能とC10K問題

第6回

  • X ポスト
  • このエントリーをはてなブックマークに追加

コネクション確立の性能

 まずはクライアントとのコネクションが確立するまでの時間を計測し、コネクション数に応じた数値の変化を観測します。ループバックアドレス(127.0.0.1)を通してコネクションを張った直後にpoll(2), epoll(7)などの多重I/O用関数を使用します。多重I/O用関数を使用する前で計測を開始、関数通過後accept(2)が終わって新たなディスクリプタが生成された直後までにかかった時間を計測します。その計測をコネクション数(=ディスクリプタ数)の増加との関わりで検証します。

 クライアント用のプロセスと分けてしまうと、accept(2)用ソケットのbacklog(listen(2)の第2引数に設定する値)に達してしまい、コネクション待ちで余計なサスペンドが発生するので測定値にばらつきが出てしまいます。今回は多重I/O用関数を使用する直前にクライアントからのconnect(2)を行うことで、backlog溢れを回避しています。使用するプログラムは、ここではpoll(2)用しか表示していませんが、それ以外のプログラム(epoll_level_conn.c、epoll_edge_conn.c)はダウンロードして使用してください。

 当検証では10,000個のディスクリプタを生成し計測しています。計測対象の多重I/O用関数は、poll(2)とepoll(2)のレベルトリガとエッジトリガとします。select(2)は仕様上の制約により、1,000程度のディスクリプタ管理までしかできませんので、当測定対象から除外しました。

 また、ディスクリプタ数がこれだけ多い場合、素の設定ではディスクリプタを生成できない可能性があります。事前に使用可能ローカルポート数の幅を60,000以上に、ファイルオープン数を30,000程度に広げておいてください。

 使用可能ローカルポート数の幅は、/proc/sys/net/ipv4/ip_local_port_rangeで設定されています。使用ファイルオープン数はulimitで設定できますが、システム上の最大数は/proc/sys/fs/file-maxにありますので、その値を参考にして設定します。実際の作業は下記を参考にしてください。

ローカルポート数の設定
% cat /proc/sys/net/ipv4/ip_local_port_range
 32768   61000
% echo "1024 65000" > /proc/sys/net/ipv4/ip_local_port_range
% cat /proc/sys/net/ipv4/ip_local_port_range
 1024    65000
% ulimit -n
 1024
% cat /proc/sys/fs/file-max
 199090
% ulimit -n `cat /proc/sys/fs/file-max`
% ulimit -n
 199090
%

 以下がpoll(2)用の性能測定用プログラムになります。epoll(2)のレベルトリガ・エッジトリガの性能測定用プログラムとの処理の流れは同じですので、詳細はダウンロードして参照してください。

poll_conn.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#include <poll.h>
#define TV2SEC(tv) ((double)((tv).tv_sec) + (double)((tv).tv_usec / 1000000.0))

int  create_sockaddr( in_port_t * port );
void client_connect( in_port_t port );
int  wrap_accept( int sock );

int main( int argc, char ** argv ) {

    if( argc < 2 || 0 == atoi( argv[1] )) {
        fprintf( stderr, "usage : %s <conn>\n", argv[0] );
        exit( 1 );
    }
    int maxconnect = atoi( argv[1] );

    struct timeval  tv1,tv2;
    struct pollfd pfds[maxconnect + 1];
    in_port_t port;
    int    fd, i;

    int sock = create_sockaddr( &port );

    memset( pfds, 0, sizeof( pfds ));
    pfds[0].fd = sock;
    pfds[0].events = POLLIN;
    pfds[0].revents = 0;
    int nfds = 1;

    for( i = 0; i < maxconnect; i ++ ) {
        client_connect( port );

        gettimeofday( &tv1, 0 );
        poll( pfds, maxconnect + 1, -1 );
        fd = wrap_accept( sock );
        gettimeofday( &tv2, 0 );

        printf( "%5d\t%f\n", i, TV2SEC( tv2 ) - TV2SEC( tv1 ));
        pfds[nfds].fd = fd;
        pfds[nfds].events = POLLIN;
        pfds[nfds].revents = 0;
        nfds ++;
    }
    return 0;
}

int create_sockaddr( in_port_t * port ) {
    struct sockaddr_in addr;
    int sock = socket( AF_INET, SOCK_STREAM, 0 );
    addr.sin_family      = AF_INET;
    addr.sin_port        = htons( 0 );
    addr.sin_addr.s_addr = htonl( INADDR_ANY );
    bind( sock, ( struct sockaddr *)&addr, sizeof( addr ));
    listen( sock, SOMAXCONN );
    socklen_t socklen = sizeof( addr );
    getsockname( sock, ( struct sockaddr *)&addr, &socklen);
    *port = addr.sin_port;
    return sock;
}

void client_connect( in_port_t port ) {
    struct sockaddr_in addr;
    int sock = socket( AF_INET, SOCK_STREAM, 0 );
    addr.sin_family = AF_INET;
    addr.sin_port   = htons( ntohs( port ));
    addr.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
    connect( sock, ( struct sockaddr *)&addr, sizeof( struct sockaddr_in ));
}

int  wrap_accept( int sock ) {
    struct sockaddr_in addr;
    socklen_t len = sizeof( addr );
    return accept( sock, ( struct sockaddr *)&addr, &len );
}
図1のデータ取得方法
# ./poll_conn 10000
# ./epoll_level_conn 10000
# ./epoll_edge_conn 10000

 計測結果は下記の通りです(以下のすべての図の横軸はコネクション数、縦軸の単位は秒です)。

図1
図1

 pollはコネクション数に応じて緩やかな角度で増えており、epollはコネクションの数に応じたレスポンス値の変化がまったくなく、0付近で推移しています。これだけを見ると、pollは非常に遅く、epollの性能の良さを認識いただけるでしょう。

次のページ
データ送受信の性能

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
ファイルディスクリプタについて連載記事一覧

もっと読む

この記事の著者

赤松 エイト(エイト)

(株)DTSに勤てます。WebアプリやJavaやLL等の上位アプリ環境を密かに憧れつつも、ず~っとLinuxとかHP-UXばかり、ここ数年はカーネル以上アプリ未満のあたりを行ったり来たりしています。mixiもやってまして、こちらは子育てとか日々の日記メインです。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/4830 2010/03/05 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング