PageRenderTime 194ms CodeModel.GetById 71ms app.highlight 62ms RepoModel.GetById 58ms app.codeStats 0ms

/mordor/examples/netbench.cpp

http://github.com/mozy/mordor
C++ | 244 lines | 199 code | 28 blank | 17 comment | 24 complexity | 562e0c87df5cfdb11eadee304e022c34 MD5 | raw file
  1#include "netbench.h"
  2
  3#include "mordor/version.h"
  4
  5#include <stdio.h>
  6#ifndef WINDOWS
  7#include <sys/resource.h>
  8#endif
  9#include <iostream>
 10
 11#include <boost/bind.hpp>
 12#include <boost/lexical_cast.hpp>
 13#include <boost/date_time/posix_time/posix_time.hpp>
 14
 15static const char *usage =
 16"iombench [-h host:port] [-n conns] [-a active] [-b bytes_to_xfer] [-s] [-c] [-p]\n\n"
 17"-h: host:port (defaults to 'localhost:9000')\n"
 18"-s: run as server\n"
 19"-c: run as client (can run as both in same process)\n"
 20"-p: perform power of 2 increase in number of connected sockets\n";
 21
 22NetBench::NetBench(int argc, char* argv[])
 23    : m_host("localhost:9000"),
 24      m_runServer(false),
 25      m_runClient(false),
 26      m_powTwo(false),
 27      m_maxConns(1),
 28      m_maxActive(0),
 29      m_numBytes(1),
 30      m_client(NULL),
 31      m_totalConns(1),
 32      m_totalActive(1),
 33      m_newConns(1),
 34      m_newActive(1),
 35      m_iters(10000)
 36{
 37    parseOptions(argc, argv);
 38}
 39
 40void
 41NetBench::parseOptions(int argc, char* argv[])
 42{
 43#ifndef WINDOWS
 44    int c;
 45    extern char *optarg;
 46
 47    if (argc == 1) {
 48        std::cerr << "usage: " << usage;
 49        exit(-1);
 50    }
 51
 52    while ((c = getopt(argc, argv, "cspn:a:b:h:")) != -1) {
 53        switch (c) {
 54            case 'n':
 55                m_maxConns = boost::lexical_cast<size_t>(optarg);
 56                break;
 57            case 'a':
 58                m_maxActive = boost::lexical_cast<size_t>(optarg);
 59                break;
 60            case 'b':
 61                m_numBytes = boost::lexical_cast<size_t>(optarg);
 62                break;
 63            case 'h':
 64                m_host = optarg;
 65                break;
 66            case 's':
 67                m_runServer = true;
 68                break;
 69            case 'c':
 70                m_runClient = true;
 71                break;
 72            case 'p':
 73                m_powTwo = true;
 74                break;
 75            default:
 76                std::cerr << "usage: " << usage;
 77                exit(-1);
 78        }
 79    }
 80#else
 81    m_runServer = true;
 82    m_runClient = true;
 83    m_powTwo = true;
 84    m_maxConns = 8000;
 85#endif
 86
 87    if (!m_runServer && !m_runClient) {
 88        std::cerr << "must run either the client or the server\n";
 89        std::cerr << "usage: " << usage;
 90        exit(-1);
 91    }
 92
 93    if (m_maxConns < m_maxActive) {
 94        std::cerr << "numConns must be >= numActive\n";
 95        exit(-1);
 96    }
 97
 98    if (m_maxActive == 0) {
 99        m_maxActive = m_maxConns;
100    }
101}
102
103void
104NetBench::run(NetBenchServer* server, NetBenchClient* client)
105{
106    std::cerr << "server "
107              << "runServer: " << (m_runServer ? "yes " : "no ")
108              << "runClient: " << (m_runClient ? "yes " : "no ")
109              << "numConns: " << m_maxConns << " "
110              << "numActive: " << m_maxActive << " "
111              << "numBytes: " << m_numBytes << "\n";
112
113#ifndef WINDOWS
114    // the extra 20 fds are just some fluff in case we are called with
115    // some small number of desired fds, like 1
116    rlim_t rlfds = ((m_runServer && m_runClient ? 2 : 1) *
117                    m_maxConns + 20);
118    struct rlimit rl;
119    rl.rlim_cur = rlfds * 2 + 20;
120    rl.rlim_max = rlfds * 2 + 20;
121    if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
122        perror("setrlimit");
123        exit(-1);
124    }
125#endif
126
127    m_server = server;
128    m_client = client;
129
130    if (m_runServer) {
131        server->run(m_host, 1, m_numBytes,
132                    boost::bind(&NetBench::serverRunning, this));
133    } else {
134        runClient();
135    }
136}
137
138void
139NetBench::serverRunning()
140{
141    if (m_runClient) {
142        runClient();
143    }
144}
145
146void
147NetBench::runClient()
148{
149    //
150    // Print headers using gnuplot comments so that the output
151    // of this benchmark can be used as a gnuplot data file.
152    //
153    std::cout << "#runServer: " << (m_runServer ? "yes " : "no ")
154              << "runClient: " << (m_runClient ? "yes " : "no ")
155              << "numConns: " << m_maxConns << " "
156              << "numActive: " << m_maxActive << " "
157              << "numBytes: " << m_numBytes << "\n"
158              << "#\n"
159              << "# total_conns total_time_sec num_ops "
160              << "avg_time_usec ops_per_sec bw_MB_per_sec\n";
161
162    m_client->init(m_host, m_numBytes, 1,
163                   boost::bind(&NetBench::initRound, this));
164}
165
166void
167NetBench::initRound()
168{
169    m_client->prepClientsForNextRound(m_newConns, m_newActive, m_iters,
170                                      boost::bind(&NetBench::clientsReady,
171                                                  this));
172}
173
174void
175NetBench::clientsReady()
176{
177    m_start = boost::posix_time::microsec_clock::universal_time();
178    m_client->startRound(boost::bind(&NetBench::roundDone, this, _1));
179}
180
181void
182NetBench::roundDone(size_t actualOps)
183{
184    boost::posix_time::ptime end;
185    end = boost::posix_time::microsec_clock::universal_time();
186    boost::posix_time::time_duration diff = end - m_start;
187
188    // Do all stat math using double to avoid rounding errors
189    // during divides
190    double active = (double)std::min(m_totalConns, m_maxActive);
191    double elapsed = (double)diff.total_microseconds();
192    double usAvg = elapsed / (active * m_iters);
193    double numOps = active * m_iters;
194    double opsSec = (numOps / elapsed) * 1000 * 1000;
195    double mb = (m_numBytes * numOps) * 1024 * 1024;
196    double bw = (mb / elapsed) / (1000 * 1000);
197
198    // This is a sanity check to make sure that our sychronization
199    // logic is correct and that we are really doing the amount
200    // of work that we think we are
201    if (actualOps != numOps) {
202        std::cerr << "actualOps != numOps ("
203                  << actualOps << " != " << numOps << ")\n";
204        abort();
205    }
206
207    std::cout
208        << m_totalConns << " "
209        << elapsed / 1000000 << " "
210        << numOps << " "
211        << usAvg << " "
212        << opsSec << " "
213        << bw << "\n";
214
215    // Figure out how many new clients to run with next round
216    if (m_powTwo) {
217        m_newConns = std::min(m_totalConns * 2 - m_totalConns,
218                              m_maxConns - m_totalConns);
219    } else {
220        m_newConns = 1;
221    }
222
223    // Figure out how many total conns and active conns for next round
224    m_totalConns += m_newConns;
225    m_newActive = std::min(m_totalConns, m_maxActive) - m_totalActive;
226    m_totalActive = m_totalActive + m_newActive;
227
228    // Figure out how many iters to run for the next round.
229    // We'll run for 1ms for non power of two based runs and
230    // a full 3s otherwise (really, for full graphs you should
231    // always run pow2)
232    unsigned long long targetUs = m_powTwo ? 3000000 : 1000;
233    m_iters = (size_t) (targetUs / m_totalActive / usAvg);
234    if (m_iters == 0) {
235        m_iters = 1;
236    }
237
238    if (m_newConns && m_totalConns <= m_maxConns) {
239        initRound();
240    } else {
241        m_client->stop();
242        m_server->stop();
243    }
244}