📄 poller_bench.cc
字号:
/*-------------------------------------------------------------------------- Copyright 1999,2000, Dan Kegel http://www.kegel.com/ See the file COPYING (Also freely licensed to Disappearing, Inc. under a separate license which allows them to do absolutely anything they want with it, without regard to the GPL.) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA--------------------------------------------------------------------------*/#include "Poller_devpoll.h"#include "Poller_kqueue.h"#include "Poller_poll.h"#include "Poller_select.h"#include "Poller_sigio.h"#include "dprint.h"#include "eclock.h"#include "CHECK.h"#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>// bogus Solaris fix#ifndef AF_LOCAL#define AF_LOCAL AF_UNIX#endifvoid die(int lineno, char method){ perror(""); printf("Error. Test %c failed at line %d.\n", method, lineno); exit(1);}#define DIE(method) die(__LINE__, method)/*---------------------------------------------------------------------- Portable function to set a socket into nonblocking mode.----------------------------------------------------------------------*/static int setNonblocking(int fd){ int flags; /* Caution: O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5. */#if defined(O_NONBLOCK) if (-1 == (flags = fcntl(fd, F_GETFL, 0))) flags = 0; return fcntl(fd, F_SETFL, flags | O_NONBLOCK);#else flags = 1; return ioctl(fd, FIOBIO, &flags);#endif}/**-------------------------------------------------------------------------- Class to run iterations of a test for about N seconds when you don't know how long it takes to run one iteration. Use with a do - while loop, e.g. IterationController ic; ic.init(10); // to specify a 10 second run do { ... whatever you're trying to benchmark ... } while (ic.anotherIterationNeeded()); printf("Each iteration took %g seconds.\n", ic.getResult());--------------------------------------------------------------------------*/class IterationController{public: /// Call this once at beginning of loop void init(int desiredRuntimeSeconds) { m_ticksPerBlock = 0; m_iterationsPerBlock = 1; m_iterationsLeft = m_iterationsPerBlock; m_eclock_hertz = eclock_hertz(); m_desiredTicksPerBlock = (m_eclock_hertz * desiredRuntimeSeconds); m_start = eclock(); //DPRINT(("IterationController::init: start %d\n", m_start)); } /// Call this at end of each iteration to see if another is needed bool anotherIterationNeeded() { //DPRINT(("IterationController::anotherIterationNeeded: m_iL %d\n", m_iterationsLeft)); if (--m_iterationsLeft > 0) return true; clock_t now = eclock(); m_ticksPerBlock = now - m_start; //DPRINT(("IterationController::anotherIterationNeeded: now %d m_tpb %d m_dtpb %d m_ipb %d\n", //now, m_ticksPerBlock, m_desiredTicksPerBlock, m_iterationsPerBlock)); if (m_ticksPerBlock < m_desiredTicksPerBlock) { m_iterationsLeft = m_iterationsPerBlock; if (m_ticksPerBlock < (m_desiredTicksPerBlock / 16)) m_iterationsLeft = (m_iterationsPerBlock * 15); // ramp up faster if way off m_iterationsPerBlock += m_iterationsLeft; //DPRINT(("IterationController::anotherIterationNeeded: m_ipb now %d\n", //m_iterationsPerBlock)); return true; } return false; } /// Call this after loop exits to get time per iteration in seconds double getResult() { //DPRINT(("IterationController::getResult: m_tpb %d m_ipb %d result %g\n", //m_ticksPerBlock, m_iterationsPerBlock, //(double) m_ticksPerBlock / ((double) m_iterationsPerBlock * m_eclock_hertz))); return (double) m_ticksPerBlock / ((double) m_iterationsPerBlock * m_eclock_hertz); }private: int m_ticksPerBlock; int m_iterationsPerBlock; int m_iterationsLeft; int m_eclock_hertz; int m_desiredTicksPerBlock; clock_t m_start;};// Clear the kernel profiling interface.bool kernelProfileActive = false;void kernelProfileStart(){ kernelProfileActive = false; // Writing to the profile file clears it. Must be root. int fd = open("/proc/profile", O_RDWR); if (fd < 0) return; if (write(fd, "", 1) != 1) { DIE('0'); } close(fd); kernelProfileActive = true;}// Collect data accumulated by the kernel profiling interface into the given file.void kernelProfileStop(const char *filename){ if (kernelProfileActive) { char buf[256]; sprintf(buf, "cp /proc/profile %s", filename); if (system(buf)) DIE('0'); kernelProfileActive = false; }}template<class T>class Poller_bench : Poller::Client{ T poller;public: int notifyPollEvent(Poller::PollEvent *event) { char buf[1]; int n; assert(event->revents & POLLIN); //DPRINT(("notifyPollEvent: reading one byte from fd %d\n", event->fd)); n=read(event->fd, buf, sizeof(buf)); // Poller_sigio does not promise that all notifications are true, // so it's ok to get falsely awoken. if (n != sizeof(buf) && errno != EAGAIN) { int err = errno; fprintf(stderr, "\nread(%d, %p, %d) returns %d?!, errno %d\n", event->fd, buf, sizeof(buf), n, err); DIE('?'); } // for the benefit of Poller_sigio, clear event flag once we've handled it poller.clearReadiness(event->fd, POLLIN); return 0; } // Return how long it took to run each loop of the test, in microseconds int bench(int num_pipes, int fdpairs[][2], int num_active, int seconds, char method) { int i, k; int spacing; int err = poller.init(); if (err) DIE(method); err = poller.setSignum(40); if (err) DIE(method); for (i = 0; i < num_pipes; i++) { // Add the read end to be monitored if (poller.add(fdpairs[i][0], this, POLLIN)) { DIE(method); } } spacing = num_pipes / num_active; int half = spacing/2; CHECK(true, (spacing >= 1)); CHECK(true, (spacing * (num_active-1) + half < num_pipes)); char oname[256]; sprintf(oname, "bench%d%c.dat", num_pipes, method); IterationController ic; kernelProfileStart(); ic.init(seconds); do { for (k=0; k<num_active; k++) if (write(fdpairs[half + k * spacing][1], "a", 1) != 1) DIE(method); if (poller.waitAndDispatchEvents(0)) DIE(method); } while (ic.anotherIterationNeeded()); kernelProfileStop(oname); return (int)(ic.getResult() * 1e6); }};int main(int argc, char **argv){ int runtime, num_active; const char *methods; int (*fdpairs)[2]; char method; setvbuf(stdout, (char *)NULL, _IONBF, 0); if (argc < 5) { printf("Usage: %s runtime nactive [psdkr] npipes [npipes...]\n", argv[0]); return 1; } if ((runtime=atoi(argv[1])) < 1) { printf("invalid runtime %s\n", argv[1]); return 1; } if ((num_active=atoi(argv[2])) < 1) { printf("invalid num_active %s\n", argv[2]); return 1; } methods = argv[3]; // Grab array of desired num_pipes values int i; int maxpipes = 0; int np[128]; for (i=4; i<argc; i++) { int p = atoi(argv[i]); np[i-4] = p; if (p > maxpipes) maxpipes = p; if (p < 1) { printf("invalid npipes %s\n", argv[i]); return 1; } if (num_active > p) { printf("num_active %d > num_pipes %d\n", num_active, p); return 1; } } np[i-4] = 0; fdpairs = new int[maxpipes][2]; if (!fdpairs) { DIE('0'); } clock_t start = eclock(); for (i = 0; i < maxpipes; i++) { // Create a pipe int fds[2]; int err = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds); if (err) { DIE('0'); } fdpairs[i][0] = fds[0]; fdpairs[i][1] = fds[1]; if (fdpairs[i][0] < 0) { printf("Bad fd %d!\n", fdpairs[i][0]); DIE('0'); } if (fdpairs[i][1] < 0) { printf("Bad fd %d!\n", fdpairs[i][1]); DIE('0'); } // This test is written so pipe reads and writes should never block. // We want to be able to check whether read or write would block, and abort. if (setNonblocking(fdpairs[i][0]) || setNonblocking(fdpairs[i][1])) DIE('0'); } int delta = (int)(eclock() - start); printf("%d microseconds to open each of %d socketpairs\n", (int)((delta * 1.0e6) / (eclock_hertz() * maxpipes)), maxpipes); printf("%10s", "pipes"); for (i=0; np[i]; i++) printf("%7d ", np[i]); printf("\n"); while ((method = *methods++) != 0) { switch (method) { case 'p': printf("%10s", "poll"); for (i=0; np[i]; i++) { Poller_bench<Poller_poll> bench_poll; printf("%7d ", bench_poll.bench(np[i], fdpairs, num_active, runtime, method)); } printf("\n"); break; case 's': printf("%10s", "select"); for (i=0; np[i]; i++) { if (np[i] * 2 < FD_SETSIZE) { Poller_bench<Poller_select> bench_select; printf("%7d ", bench_select.bench(np[i], fdpairs, num_active, runtime, method)); } else printf("%7s ", "-"); } printf("\n"); break;#if HAVE_DEVPOLL case 'd': printf("%10s", "/dev/poll"); for (i=0; np[i]; i++) { Poller_bench<Poller_devpoll> bench_devpoll; printf("%7d ", bench_devpoll.bench(np[i], fdpairs, num_active, runtime, method)); } printf("\n"); break;#endif#if HAVE_KQUEUE case 'k': printf("%10s", "kqueue"); for (i=0; np[i]; i++) { Poller_bench<Poller_kqueue> bench_kqueue; printf("%7d ", bench_kqueue.bench(np[i], fdpairs, num_active, runtime, method)); break; } printf("\n"); break;#endif#if HAVE_F_SETSIG case 'r': printf("%10s", "rtsig"); for (i=0; np[i]; i++) { Poller_bench<Poller_sigio> bench_sigio; printf("%7d ", bench_sigio.bench(np[i], fdpairs, num_active, runtime, method)); break; } printf("\n"); break;#endif default: printf("Method %c unsupported on this platform.\n", method); } } start = eclock(); for (i=0; i<maxpipes; i++) { if (close(fdpairs[i][0])) DIE('0'); if (close(fdpairs[i][1])) DIE('0'); } delta = (int)(eclock() - start); printf("%d microseconds to close each of %d socketpairs\n", (int)((delta * 1.0e6) / (eclock_hertz() * maxpipes)), maxpipes); delete [] fdpairs; exit(0);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -