Выбрать главу

13.1.6 Сравнение poll() и epoll

Методы poll() и epoll существенно отличаются; poll() хорошо стандартизован, но плохо масштабируется, в то время как epoll существует только в Linux, но очень хорошо масштабируется. Приложения, наблюдающие за небольшим количеством файловых дескрипторов и переносимости величин, должны использовать poll(), но любому приложению, которому необходимо контролировать большое количество дескрипторов, лучше применять epoll, даже если ему нужно поддерживать poll() для других платформ.

Отличия в производительности двух методов поразительны. Чтобы продемонстрировать, насколько лучше масштабируется epoll, в коде poll-vs-epoll.с измеряется количество системных вызовов poll() и epoll_wait(), которые можно создать за одну секунду для наборов файловых дескрипторов разных размеров (количество файловых дескрипторов для помещения в набор задается в командной строке). Каждый файловый дескриптор ссылается на считывающую часть канала, и они создаются с помощью dup2().

В табл. 13.1 суммируются результаты запуска poll-vs-epoll.с для установленных размеров диапазоном от одного до 100 000 файловых дескрипторов[82]. В то время как количество системных вызовов в секунду резко падает для poll(), оно остается почти постоянным для epoll[83]. Как поясняет эта таблица, epoll добавляет в систему намного меньше нагрузки, чем poll(), и в результате гораздо лучше масштабируется.

Таблица 13.1. Результаты сравнения poll() и epoll()

Файловые дескрипторы poll() epoll()
1 310063 714848
10 140842 726108
100 25866 726659
1000 3343 729072
5000 612 718424
10000 300 730483
25000 108 717097
50000 38 729746
100000 18 712301

  1: /* poll-vs-epoll.с */

  2:

  3: #include <errno.h>

  4: #include <fcntl.h>

  5: #include <stdio.h>

  6: #include <sys/epoll.h>

  7: #include <sys/poll.h>

  8: #include <sys/signal.h>

  9: #include <unistd.h>

 10: #include <sys/resource.h>

 11: #include <string.h>

 12: #include <stdlib.h>

 13:

 14: #include <sys/select.h>

 15:

 16: int gotAlarm;

 17:

 18: void catch(int sig) {

 19:  gotAlarm = 1;

 20: }

 21:

 22: #define OFFSET 10

 23:

 24: int main(int argc, const char ** argv) {

 25:  int pipeFds[2];

 26:  int count;

 27:  int numFds;

 28:  struct pollfd * pollFds;

 29:  struct epoll_event event;

 30:  int epfd;

 31:  int i;

 32:  struct rlimit lim;

 33:  char * end;

 34:

 35:  if (!argv[1]) {

 36:   fprintf(stderr, "ожидалось число\n");

 37:   return 1;

 38:  }

 39:

 40:  numFds = strtol(argv[1], &end, 0);

 41:  if (*end) {

 42:   fprintf(stderr, "ожидалось число\n");

 43:   return 1;

 44:  }

 45:

 46:  printf("Запуск теста для %d файловых дескрипторов.\n", numFds);

 47:

 48:  lim.rlim_cur = numFds + OFFSET;

 49:  lim.rlim_max = numFds + OFFSET;

 50:  if (setrlimit(RLIMIT_NOFILE, &lim)) {

 51:   perror("setrlimit");

 52:   exit(1);

 53:  }

 54:

 55:  pipe(pipeFds);

 56:

 57:  pollFds = malloc(sizeof (*pollFds) * numFds);

 58:

 59:  epfd = epoll_create(numFds);

 60:  event.events = EPOLLIN;

 61:

 62:  for (i = OFFSET; i < OFFSET + numFds; i++) {

 63:   if (dup2(pipeFds[0], i) != i) {

 64:    printf("сбой в %d: %s\n", i, strerror(errno));

 65:    exit(1);

 66:   }

 67:

 68:   pollFds[i - OFFSET].fd = i;

 69:   pollFds[i - OFFSET].events = POLLIN;

 70:

 71:   event.data.fd = i;

 72:   epoll_ctl(epfd, EPOLL_CTL_ADD, i, &event);

 73:  }

 74:

 75:  /* с помощью signal выяснить, когда время истекло */

 76:  signal(SIGALRM, catch);

 77:

 78:  count = 0;

 79:  gotAlarm = 0;

 80:  alarm(1);

 81:  while (!gotAlarm) {

вернуться

82

Эту программу необходимо запускать от имени root для наборов, содержащих более 1000 дескрипторов.

вернуться

83

Настоящее тестирование не гарантирует статистическую точность. Был проведен лишь один тестовый прогон, поэтому результаты поначалу будут неустойчивыми, что, однако, исчезнет после большого количества повторов.