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