FreeBSD系にはselectに代わるI/O多重化の手段としてkeventも用意されている。keventの方がselectよりパフォーマンスが良いという噂を耳にしたので、計測してみた。
正直ここまで違うとは思ってなかった。監視対象のfdの数が高々10個の場合でも約2倍の性能差が出ている。
しかもてっきりfdの数に対して線形で時間が増えると予想していたが、selectの方はそれ以上のペースで時間がかかるようになっている。
selectにはウォッチできる数の制限もあるみたいだし、規模が大きくなるとポータビリティを犠牲にしてでもkeventに置き換えないといけないケースもありそうだ。
なお、計測はFreeBSDマシンが手元になかったので以下の環境で行っている。
OS: Mac OS X(10.7.5)
CPU: 2.7 GHz Intel Core i7
Memory: 4GB 1333 MHz DDR3
念のため計測時のコードもさらしておく。
#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/event.h>
#include <sys/time.h>
#define COUNT 10000
#define PIPE_NUM 100
//#define USE_SELECT
static quad_t
gettimeofday_msec (void)
{
struct timeval tv;
gettimeofday (&tv, NULL);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
int
main (void)
{
int fildes[PIPE_NUM][2];
int i,j;
int dummy = 1234;
quad_t t1,t2;
fd_set deffds;
int max_fd = 0;
struct kevent kev;
int kq;
#if defined(USE_SELECT)
FD_ZERO (&deffds);
#else
kq = kqueue ();
#endif
for (i = 0; i < PIPE_NUM; i++)
{
pipe(fildes[i]);
#if defined(USE_SELECT)
FD_SET (fildes[i][0], &deffds);
max_fd = max_fd < fildes[i][0] ? fildes[i][0] : max_fd;
#else
EV_SET (&kev, fildes[i][0], EVFILT_READ, EV_ADD, 0, 0, NULL);
kevent (kq, &kev, 1, NULL, 0, NULL);
#endif
}
t1 = gettimeofday_msec ();
for (i = 0; i < COUNT; i++)
{
for (j = 0; j < PIPE_NUM; j++)
{
write (fildes[j][1], &dummy, sizeof(dummy));
#if defined(USE_SELECT)
fd_set fdset = deffds;
select (max_fd+1, &fdset, NULL, NULL, NULL);
#else
kevent(kq, NULL, 0, &kev, 1, NULL);
#endif
read (fildes[j][0], &dummy, sizeof(dummy));
}
}
t2 = gettimeofday_msec ();
#if defined(USE_SELECT)
printf ("Result(select): ");
#else
printf ("Result(kevent): ");
#endif
printf ("PIPE_NUM = %d, COUNT = %d -> %lld msec\n", PIPE_NUM, COUNT, t2 - t1);
return 0;
}