On Thu, Dec 13, 2001 at 07:03:32PM +0300, Eugene B. Berdnikov wrote:
> On Thu, Dec 13, 2001 at 05:29:23PM +0300, AT wrote:
> > Ещё я исследовал поведение poll(2) и select(2) на линуксе, при небольшом
> > числе дескрипторов они работали примерно одинаково, а при большом --
> > select сильно обгонял poll. Вот какие данные примерно получались:
> >
> > select (empty) 3.030 usec 0.330 Mhz
> > select (16 desc.) 3.048 usec 0.328 Mhz
> > select (512 desc.) 4.055 usec 0.247 Mhz
> > poll (empty) 1.180 usec 0.848 Mhz
> > poll (16 desc.) 10.608 usec 0.094 Mhz
> > poll (512 desc.) 122.048 usec 0.008 Mhz
>
> Что это за попугаи? Время возврата из сискола при одном активном fd?
Типа того! Вы будете смеяться: тест идёт на неинициализированных
дескрипторах. Т.е. на произвольном куске памяти. Но этого как раз и
хотелось посмотреть: насколько poll и select отличаются сами по себе,
помимо их общего знаменателя. Цифры даны для того, чтобы почувствовать
порядок разницы. Они подтверждают догадку о том, что узкое место --
копирование в ядро.
Тест прилагаю.
Что я хочу сказать: цифры эти не всегда имеют практическое значение.
poll начинает существенно проигрывать selet только при числе
одновременно открытых дескрипторов большем 16. При меньшем -- poll даже
выигрывает, но это несущественно.
Нужно только понимать, что 16 одновременных дескрипторов для httpd --
это трафик порядка 1Mbps и 100Gb в месяц. Т.е. это достаточно много. И
если такой нагрузки нет, то беспокоиться о преимуществе select над poll
совсем не стоит. :)
>
> Киньте этот тест, мы его сами погоняем. И линукс не забудем.
> --
> Eugene Berdnikov
__
AT
/* by Michael Lee, 1997-01-23 */
/* Must compile with optimization off, e.g. "gcc speed.c -lm -o speed" */
/* For best results: run twice in a row, ignore first batch of results. */
/* This is not a benchmark in any way, shape or form. */
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <math.h>
#include <sys/select.h>
#include <sys/poll.h>
#define MIN_DURATION 10.0 /* repeat each text for at least this many seconds */
#define MIN_ITER 20 /* or at least this often, whichever is greater */
#define SIGDIGIT 5 /* don't round when first non-zero digit
of precision is at least this much */
/* CLK_TCK was documented in K&R2 first printing, but was later renamed. */
/* Old but otherwise okay C89 compilers may still use it */
#ifndef CLOCKS_PER_SEC
#define CLOCKS_PER_SEC CLK_TCK
#endif
/* some variables used by the macros, below */
static clock_t op_start, op_end;
static double baseline, usec, resolution;
static int i, prec;
/* macros for starting and ending each particular test; made them
into macros so the rest of the code isn't full of tedious bookkeeping
stuff. */
#define BEGIN_TEST(name) printf("%20s ", name); fflush(stdout); \
i = 0; op_start = clock(); \
while(clock() == op_start) /* (busy) wait for clock to turn over */; \
op_start = clock(); \
while ((op_end = clock()) - op_start < CLOCKS_PER_SEC * MIN_DURATION) { \
int q; \
for (q=0; q < MIN_ITER; q++) {
#define END_TEST(name) i++; } } \
usec = \
1000000.0 * (double) (op_end - op_start - baseline * i) / \
(double) CLOCKS_PER_SEC / (double) i; \
/* just zero out anything too inherently noisy to be useful */ \
if (usec <= 0.0) usec = 0.0; \
printf("%9.3f usec", usec); \
if (usec > 0.0) printf("%9.3f Mhz\n", 1.0 / usec); \
else printf(" n/a\n");
/* empty function, had to put it somewhere */
void funccall()
{
return;
}
int main(argc, argv)
int argc;
char * argv[];
{
double tempres;
int x;
char buf[80];
float foo;
float bar = 1.23456789;
int able;
int baker = 1234;
char * charlie;
FILE * fd;
fd_set readfds[512];
fd_set writefds[512];
struct pollfd ufds[512];
struct timeval timeout;
srand(1);
/* simple arithmetic to figure out how precise the measurements
might be */
printf("clocks_per_sec = %d\n", CLOCKS_PER_SEC);
resolution = 1000000.0 / CLOCKS_PER_SEC / MIN_ITER;
printf("worst case resolution = %5.4f usec\n", resolution);
/* figure out how many significant digits to output */
prec = 0;
for (tempres = resolution; tempres < SIGDIGIT; tempres *= 10)
{
prec ++;
}
printf("precision = %d decimal digits\n", prec);
/* this could happen, so we might as well check */
if (clock() == -1)
{
fprintf(stderr, "clock() is broken.\n");
exit(1);
}
baseline = 0;
/* we need to do this because the first time through a program
there is a significant penalty while the system loads needed
resources. */
BEGIN_TEST("(cache & vm load)");
END_TEST("(cache & vm load)");
/* figure out the loop overhead so that we can deduct it
from the remainder of the tests. sometimes this measurement is
noisy because it's so trivial. increase MIN_DURATION if it's
a problem */
BEGIN_TEST("(loop overhead)");
/* nothing */;
END_TEST("(loop overhead)");
baseline = (double) (op_end - op_start) / (double) i;
BEGIN_TEST("memset overhead");
memset(&timeout, 0, sizeof timeout);
END_TEST("memset overhead");
BEGIN_TEST("select (empty)");
memset(&timeout, 0, sizeof timeout);
select(0, readfds, writefds, NULL, &timeout);
END_TEST("select (empty)");
BEGIN_TEST("select (16 desc.)");
memset(&timeout, 0, sizeof timeout);
select(16, readfds, writefds, NULL, &timeout);
END_TEST("select (16 desc.)");
BEGIN_TEST("select (512 desc.)");
memset(&timeout, 0, sizeof timeout);
select(512, readfds, writefds, NULL, &timeout);
END_TEST("select (512 desc.)");
BEGIN_TEST("poll (empty)");
poll(ufds, 0, 0);
END_TEST("poll (empty)");
BEGIN_TEST("poll (16 desc.)");
poll(ufds, 16, 0);
END_TEST("poll (16 desc.)");
BEGIN_TEST("poll (512 desc.)");
poll(ufds, 512, 0);
END_TEST("poll (512 desc.)");
return 0;
}
"Russian Apache" includes software developed
by the Apache Group for use in the Apache HTTP server project
(http://www.apache.org/) See
Apache LICENSE.
Copyright (C) 1995-2001 The Apache Group. All rights reserved.
Copyright (C) 1996 Dm. Kryukov; Copyright (C)
1997-2009 Alex Tutubalin. Design (C) 1998 Max Smolev.