benchmark-pound.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
  2. *
  3. * Permission is hereby granted, free of charge, to any person obtaining a copy
  4. * of this software and associated documentation files (the "Software"), to
  5. * deal in the Software without restriction, including without limitation the
  6. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  7. * sell copies of the Software, and to permit persons to whom the Software is
  8. * furnished to do so, subject to the following conditions:
  9. *
  10. * The above copyright notice and this permission notice shall be included in
  11. * all copies or substantial portions of the Software.
  12. *
  13. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  18. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  19. * IN THE SOFTWARE.
  20. */
  21. #include "task.h"
  22. #include "uv.h"
  23. /* Update this is you're going to run > 1000 concurrent requests. */
  24. #define MAX_CONNS 1000
  25. #undef NANOSEC
  26. #define NANOSEC ((uint64_t) 1e9)
  27. #undef DEBUG
  28. #define DEBUG 0
  29. struct conn_rec_s;
  30. typedef void (*setup_fn)(int num, void* arg);
  31. typedef void (*make_connect_fn)(struct conn_rec_s* conn);
  32. typedef int (*connect_fn)(int num, make_connect_fn make_connect, void* arg);
  33. /* Base class for tcp_conn_rec and pipe_conn_rec.
  34. * The ordering of fields matters!
  35. */
  36. typedef struct conn_rec_s {
  37. int i;
  38. uv_connect_t conn_req;
  39. uv_write_t write_req;
  40. make_connect_fn make_connect;
  41. uv_stream_t stream;
  42. } conn_rec;
  43. typedef struct {
  44. int i;
  45. uv_connect_t conn_req;
  46. uv_write_t write_req;
  47. make_connect_fn make_connect;
  48. uv_tcp_t stream;
  49. } tcp_conn_rec;
  50. typedef struct {
  51. int i;
  52. uv_connect_t conn_req;
  53. uv_write_t write_req;
  54. make_connect_fn make_connect;
  55. uv_pipe_t stream;
  56. } pipe_conn_rec;
  57. static char buffer[] = "QS";
  58. static uv_loop_t* loop;
  59. static tcp_conn_rec tcp_conns[MAX_CONNS];
  60. static pipe_conn_rec pipe_conns[MAX_CONNS];
  61. static uint64_t start; /* in ms */
  62. static int closed_streams;
  63. static int conns_failed;
  64. static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size);
  65. static void connect_cb(uv_connect_t* conn_req, int status);
  66. static void read_cb(uv_stream_t* stream, ssize_t nread, uv_buf_t buf);
  67. static void close_cb(uv_handle_t* handle);
  68. static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) {
  69. static char slab[65536];
  70. uv_buf_t buf;
  71. buf.base = slab;
  72. buf.len = sizeof(slab);
  73. return buf;
  74. }
  75. static void after_write(uv_write_t* req, int status) {
  76. if (status != 0) {
  77. fprintf(stderr, "write error %s\n", uv_err_name(uv_last_error(loop)));
  78. uv_close((uv_handle_t*)req->handle, close_cb);
  79. conns_failed++;
  80. return;
  81. }
  82. }
  83. static void connect_cb(uv_connect_t* req, int status) {
  84. conn_rec* conn;
  85. uv_buf_t buf;
  86. int r;
  87. if (status != 0) {
  88. #if DEBUG
  89. fprintf(stderr,
  90. "connect error %s\n",
  91. uv_err_name(uv_last_error(uv_default_loop())));
  92. #endif
  93. uv_close((uv_handle_t*)req->handle, close_cb);
  94. conns_failed++;
  95. return;
  96. }
  97. ASSERT(req != NULL);
  98. ASSERT(status == 0);
  99. conn = (conn_rec*)req->data;
  100. ASSERT(conn != NULL);
  101. #if DEBUG
  102. printf("connect_cb %d\n", conn->i);
  103. #endif
  104. r = uv_read_start(&conn->stream, alloc_cb, read_cb);
  105. ASSERT(r == 0);
  106. buf.base = buffer;
  107. buf.len = sizeof(buffer) - 1;
  108. r = uv_write(&conn->write_req, &conn->stream, &buf, 1, after_write);
  109. ASSERT(r == 0);
  110. }
  111. static void read_cb(uv_stream_t* stream, ssize_t nread, uv_buf_t buf) {
  112. uv_err_t err = uv_last_error(loop);
  113. ASSERT(stream != NULL);
  114. #if DEBUG
  115. printf("read_cb %d\n", p->i);
  116. #endif
  117. uv_close((uv_handle_t*)stream, close_cb);
  118. if (nread == -1) {
  119. if (err.code == UV_EOF) {
  120. ;
  121. } else if (err.code == UV_ECONNRESET) {
  122. conns_failed++;
  123. } else {
  124. fprintf(stderr, "read error %s\n", uv_err_name(uv_last_error(loop)));
  125. ASSERT(0);
  126. }
  127. }
  128. }
  129. static void close_cb(uv_handle_t* handle) {
  130. conn_rec* p = (conn_rec*)handle->data;
  131. ASSERT(handle != NULL);
  132. closed_streams++;
  133. #if DEBUG
  134. printf("close_cb %d\n", p->i);
  135. #endif
  136. if (uv_now(loop) - start < 10000) {
  137. p->make_connect(p);
  138. }
  139. }
  140. static void tcp_do_setup(int num, void* arg) {
  141. int i;
  142. for (i = 0; i < num; i++) {
  143. tcp_conns[i].i = i;
  144. }
  145. }
  146. static void pipe_do_setup(int num, void* arg) {
  147. int i;
  148. for (i = 0; i < num; i++) {
  149. pipe_conns[i].i = i;
  150. }
  151. }
  152. static void tcp_make_connect(conn_rec* p) {
  153. struct sockaddr_in addr;
  154. int r;
  155. r = uv_tcp_init(loop, (uv_tcp_t*)&p->stream);
  156. ASSERT(r == 0);
  157. addr = uv_ip4_addr("127.0.0.1", TEST_PORT);
  158. r = uv_tcp_connect(&((tcp_conn_rec*)p)->conn_req, (uv_tcp_t*)&p->stream, addr, connect_cb);
  159. if (r) {
  160. fprintf(stderr, "uv_tcp_connect error %s\n",
  161. uv_err_name(uv_last_error(loop)));
  162. ASSERT(0);
  163. }
  164. #if DEBUG
  165. printf("make connect %d\n", p->i);
  166. #endif
  167. p->conn_req.data = p;
  168. p->write_req.data = p;
  169. p->stream.data = p;
  170. }
  171. static void pipe_make_connect(conn_rec* p) {
  172. int r;
  173. r = uv_pipe_init(loop, (uv_pipe_t*)&p->stream, 0);
  174. ASSERT(r == 0);
  175. uv_pipe_connect(&((pipe_conn_rec*)p)->conn_req, (uv_pipe_t*)&p->stream, TEST_PIPENAME, connect_cb);
  176. #if DEBUG
  177. printf("make connect %d\n", p->i);
  178. #endif
  179. p->conn_req.data = p;
  180. p->write_req.data = p;
  181. p->stream.data = p;
  182. }
  183. static int tcp_do_connect(int num, make_connect_fn make_connect, void* arg) {
  184. int i;
  185. for (i = 0; i < num; i++) {
  186. tcp_make_connect((conn_rec*)&tcp_conns[i]);
  187. tcp_conns[i].make_connect = make_connect;
  188. }
  189. return 0;
  190. }
  191. static int pipe_do_connect(int num, make_connect_fn make_connect, void* arg) {
  192. int i;
  193. for (i = 0; i < num; i++) {
  194. pipe_make_connect((conn_rec*)&pipe_conns[i]);
  195. pipe_conns[i].make_connect = make_connect;
  196. }
  197. return 0;
  198. }
  199. static int pound_it(int concurrency,
  200. const char* type,
  201. setup_fn do_setup,
  202. connect_fn do_connect,
  203. make_connect_fn make_connect,
  204. void* arg) {
  205. double secs;
  206. int r;
  207. uint64_t start_time; /* in ns */
  208. uint64_t end_time;
  209. loop = uv_default_loop();
  210. uv_update_time(loop);
  211. start = uv_now(loop);
  212. /* Run benchmark for at least five seconds. */
  213. start_time = uv_hrtime();
  214. do_setup(concurrency, arg);
  215. r = do_connect(concurrency, make_connect, arg);
  216. ASSERT(!r);
  217. uv_run(loop, UV_RUN_DEFAULT);
  218. end_time = uv_hrtime();
  219. /* Number of fractional seconds it took to run the benchmark. */
  220. secs = (double)(end_time - start_time) / NANOSEC;
  221. LOGF("%s-conn-pound-%d: %.0f accepts/s (%d failed)\n",
  222. type,
  223. concurrency,
  224. closed_streams / secs,
  225. conns_failed);
  226. MAKE_VALGRIND_HAPPY();
  227. return 0;
  228. }
  229. BENCHMARK_IMPL(tcp4_pound_100) {
  230. return pound_it(100, "tcp", tcp_do_setup, tcp_do_connect, tcp_make_connect, NULL);
  231. }
  232. BENCHMARK_IMPL(tcp4_pound_1000) {
  233. return pound_it(1000, "tcp", tcp_do_setup, tcp_do_connect, tcp_make_connect, NULL);
  234. }
  235. BENCHMARK_IMPL(pipe_pound_100) {
  236. return pound_it(100, "pipe", pipe_do_setup, pipe_do_connect, pipe_make_connect, NULL);
  237. }
  238. BENCHMARK_IMPL(pipe_pound_1000) {
  239. return pound_it(1000, "pipe", pipe_do_setup, pipe_do_connect, pipe_make_connect, NULL);
  240. }