Home | History | Annotate | Line # | Download | only in test
      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 
     22 #include "task.h"
     23 #include "uv.h"
     24 
     25 /* Update this is you're going to run > 1000 concurrent requests. */
     26 #define MAX_CONNS 1000
     27 
     28 #undef NANOSEC
     29 #define NANOSEC ((uint64_t) 1e9)
     30 
     31 #undef DEBUG
     32 #define DEBUG 0
     33 
     34 struct conn_rec_s;
     35 
     36 typedef void (*setup_fn)(int num, void* arg);
     37 typedef void (*make_connect_fn)(struct conn_rec_s* conn);
     38 typedef int (*connect_fn)(int num, make_connect_fn make_connect, void* arg);
     39 
     40 /* Base class for tcp_conn_rec and pipe_conn_rec.
     41  * The ordering of fields matters!
     42  */
     43 typedef struct conn_rec_s {
     44   int i;
     45   uv_connect_t conn_req;
     46   uv_write_t write_req;
     47   make_connect_fn make_connect;
     48   uv_stream_t stream;
     49 } conn_rec;
     50 
     51 typedef struct {
     52   int i;
     53   uv_connect_t conn_req;
     54   uv_write_t write_req;
     55   make_connect_fn make_connect;
     56   uv_tcp_t stream;
     57 } tcp_conn_rec;
     58 
     59 typedef struct {
     60   int i;
     61   uv_connect_t conn_req;
     62   uv_write_t write_req;
     63   make_connect_fn make_connect;
     64   uv_pipe_t stream;
     65 } pipe_conn_rec;
     66 
     67 static char buffer[] = "QS";
     68 
     69 static uv_loop_t* loop;
     70 
     71 static tcp_conn_rec tcp_conns[MAX_CONNS];
     72 static pipe_conn_rec pipe_conns[MAX_CONNS];
     73 
     74 static uint64_t start; /* in ms  */
     75 static int closed_streams;
     76 static int conns_failed;
     77 
     78 static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf);
     79 static void connect_cb(uv_connect_t* conn_req, int status);
     80 static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf);
     81 static void close_cb(uv_handle_t* handle);
     82 
     83 
     84 static void alloc_cb(uv_handle_t* handle,
     85                      size_t suggested_size,
     86                      uv_buf_t* buf) {
     87   static char slab[65536];
     88   buf->base = slab;
     89   buf->len = sizeof(slab);
     90 }
     91 
     92 
     93 static void after_write(uv_write_t* req, int status) {
     94   if (status != 0) {
     95     fprintf(stderr, "write error %s\n", uv_err_name(status));
     96     uv_close((uv_handle_t*)req->handle, close_cb);
     97     conns_failed++;
     98     return;
     99   }
    100 }
    101 
    102 
    103 static void connect_cb(uv_connect_t* req, int status) {
    104   conn_rec* conn;
    105   uv_buf_t buf;
    106   int r;
    107 
    108   if (status != 0) {
    109 #if DEBUG
    110     fprintf(stderr, "connect error %s\n", uv_err_name(status));
    111 #endif
    112     uv_close((uv_handle_t*)req->handle, close_cb);
    113     conns_failed++;
    114     return;
    115   }
    116 
    117   ASSERT_NOT_NULL(req);
    118   ASSERT(status == 0);
    119 
    120   conn = (conn_rec*)req->data;
    121   ASSERT_NOT_NULL(conn);
    122 
    123 #if DEBUG
    124   printf("connect_cb %d\n", conn->i);
    125 #endif
    126 
    127   r = uv_read_start(&conn->stream, alloc_cb, read_cb);
    128   ASSERT(r == 0);
    129 
    130   buf.base = buffer;
    131   buf.len = sizeof(buffer) - 1;
    132 
    133   r = uv_write(&conn->write_req, &conn->stream, &buf, 1, after_write);
    134   ASSERT(r == 0);
    135 }
    136 
    137 
    138 static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
    139 
    140   ASSERT_NOT_NULL(stream);
    141 
    142 #if DEBUG
    143   printf("read_cb %d\n", p->i);
    144 #endif
    145 
    146   uv_close((uv_handle_t*)stream, close_cb);
    147 
    148   if (nread < 0) {
    149     if (nread == UV_EOF) {
    150       ;
    151     } else if (nread == UV_ECONNRESET) {
    152       conns_failed++;
    153     } else {
    154       fprintf(stderr, "read error %s\n", uv_err_name(nread));
    155       ASSERT(0);
    156     }
    157   }
    158 }
    159 
    160 
    161 static void close_cb(uv_handle_t* handle) {
    162   conn_rec* p = (conn_rec*)handle->data;
    163 
    164   ASSERT_NOT_NULL(handle);
    165   closed_streams++;
    166 
    167 #if DEBUG
    168   printf("close_cb %d\n", p->i);
    169 #endif
    170 
    171   if (uv_now(loop) - start < 10000) {
    172     p->make_connect(p);
    173   }
    174 }
    175 
    176 
    177 static void tcp_do_setup(int num, void* arg) {
    178   int i;
    179 
    180   for (i = 0; i < num; i++) {
    181     tcp_conns[i].i = i;
    182   }
    183 }
    184 
    185 
    186 static void pipe_do_setup(int num, void* arg) {
    187   int i;
    188 
    189   for (i = 0; i < num; i++) {
    190     pipe_conns[i].i = i;
    191   }
    192 }
    193 
    194 
    195 static void tcp_make_connect(conn_rec* p) {
    196   struct sockaddr_in addr;
    197   tcp_conn_rec* tp;
    198   int r;
    199 
    200   tp = (tcp_conn_rec*) p;
    201 
    202   r = uv_tcp_init(loop, (uv_tcp_t*)&p->stream);
    203   ASSERT(r == 0);
    204 
    205   ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
    206 
    207   r = uv_tcp_connect(&tp->conn_req,
    208                      (uv_tcp_t*) &p->stream,
    209                      (const struct sockaddr*) &addr,
    210                      connect_cb);
    211   if (r) {
    212     fprintf(stderr, "uv_tcp_connect error %s\n", uv_err_name(r));
    213     ASSERT(0);
    214   }
    215 
    216 #if DEBUG
    217   printf("make connect %d\n", p->i);
    218 #endif
    219 
    220   p->conn_req.data = p;
    221   p->write_req.data = p;
    222   p->stream.data = p;
    223 }
    224 
    225 
    226 static void pipe_make_connect(conn_rec* p) {
    227   int r;
    228 
    229   r = uv_pipe_init(loop, (uv_pipe_t*)&p->stream, 0);
    230   ASSERT(r == 0);
    231 
    232   uv_pipe_connect(&((pipe_conn_rec*) p)->conn_req,
    233                   (uv_pipe_t*) &p->stream,
    234                   TEST_PIPENAME,
    235                   connect_cb);
    236 
    237 #if DEBUG
    238   printf("make connect %d\n", p->i);
    239 #endif
    240 
    241   p->conn_req.data = p;
    242   p->write_req.data = p;
    243   p->stream.data = p;
    244 }
    245 
    246 
    247 static int tcp_do_connect(int num, make_connect_fn make_connect, void* arg) {
    248   int i;
    249 
    250   for (i = 0; i < num; i++) {
    251     tcp_make_connect((conn_rec*)&tcp_conns[i]);
    252     tcp_conns[i].make_connect = make_connect;
    253   }
    254 
    255   return 0;
    256 }
    257 
    258 
    259 static int pipe_do_connect(int num, make_connect_fn make_connect, void* arg) {
    260   int i;
    261 
    262   for (i = 0; i < num; i++) {
    263     pipe_make_connect((conn_rec*)&pipe_conns[i]);
    264     pipe_conns[i].make_connect = make_connect;
    265   }
    266 
    267   return 0;
    268 }
    269 
    270 
    271 static int pound_it(int concurrency,
    272                     const char* type,
    273                     setup_fn do_setup,
    274                     connect_fn do_connect,
    275                     make_connect_fn make_connect,
    276                     void* arg) {
    277   double secs;
    278   int r;
    279   uint64_t start_time; /* in ns */
    280   uint64_t end_time;
    281 
    282   loop = uv_default_loop();
    283 
    284   uv_update_time(loop);
    285   start = uv_now(loop);
    286 
    287   /* Run benchmark for at least five seconds. */
    288   start_time = uv_hrtime();
    289 
    290   do_setup(concurrency, arg);
    291 
    292   r = do_connect(concurrency, make_connect, arg);
    293   ASSERT(!r);
    294 
    295   uv_run(loop, UV_RUN_DEFAULT);
    296 
    297   end_time = uv_hrtime();
    298 
    299   /* Number of fractional seconds it took to run the benchmark. */
    300   secs = (double)(end_time - start_time) / NANOSEC;
    301 
    302   fprintf(stderr, "%s-conn-pound-%d: %.0f accepts/s (%d failed)\n",
    303           type,
    304           concurrency,
    305           closed_streams / secs,
    306           conns_failed);
    307   fflush(stderr);
    308 
    309   MAKE_VALGRIND_HAPPY();
    310   return 0;
    311 }
    312 
    313 
    314 BENCHMARK_IMPL(tcp4_pound_100) {
    315   return pound_it(100,
    316                   "tcp",
    317                   tcp_do_setup,
    318                   tcp_do_connect,
    319                   tcp_make_connect,
    320                   NULL);
    321 }
    322 
    323 
    324 BENCHMARK_IMPL(tcp4_pound_1000) {
    325   return pound_it(1000,
    326                   "tcp",
    327                   tcp_do_setup,
    328                   tcp_do_connect,
    329                   tcp_make_connect,
    330                   NULL);
    331 }
    332 
    333 
    334 BENCHMARK_IMPL(pipe_pound_100) {
    335   return pound_it(100,
    336                   "pipe",
    337                   pipe_do_setup,
    338                   pipe_do_connect,
    339                   pipe_make_connect,
    340                   NULL);
    341 }
    342 
    343 
    344 BENCHMARK_IMPL(pipe_pound_1000) {
    345   return pound_it(1000,
    346                   "pipe",
    347                   pipe_do_setup,
    348                   pipe_do_connect,
    349                   pipe_make_connect,
    350                   NULL);
    351 }
    352