sel_spd.c (13928B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 /* 7 * Test the speed of select within NSPR 8 * 9 */ 10 11 #include "nspr.h" 12 #include "prpriv.h" 13 14 #include <stdlib.h> 15 #include <stdio.h> 16 #include <errno.h> 17 #include <string.h> 18 19 #if defined(XP_UNIX) 20 # include <unistd.h> 21 #endif 22 23 #ifdef DEBUG 24 # define PORT_INC_DO +100 25 #else 26 # define PORT_INC_DO 27 #endif 28 #ifdef IS_64 29 # define PORT_INC_3264 +200 30 #else 31 # define PORT_INC_3264 32 #endif 33 34 #define PORT_BASE 19000 PORT_INC_DO PORT_INC_3264 35 36 typedef struct timer_slot_t { 37 unsigned long d_connect; 38 unsigned long d_cl_data; 39 unsigned long d_sv_data; 40 unsigned long d_close; 41 unsigned long d_total; 42 unsigned long requests; 43 } timer_slot_t; 44 45 static long _iterations = 5; 46 static long _client_data = 8192; 47 48 static long _server_data = (128 * 1024); 49 static long _threads_max = 10, _threads = 10; 50 51 static int verbose = 0; 52 static PRMonitor* exit_cv; 53 static long _thread_exit_count; 54 static timer_slot_t* timer_data; 55 static PRThreadScope scope1, scope2; 56 57 void tally_results(int); 58 59 /* return the diff in microseconds */ 60 unsigned long _delta(PRIntervalTime* start, PRIntervalTime* stop) { 61 /* 62 * Will C do the right thing with unsigned arithemtic? 63 */ 64 return PR_IntervalToMicroseconds(*stop - *start); 65 } 66 67 int _readn(PRFileDesc* sock, char* buf, int len) { 68 int rem; 69 int bytes; 70 71 for (rem = len; rem; rem -= bytes) { 72 bytes = PR_Recv(sock, buf + len - rem, rem, 0, PR_INTERVAL_NO_TIMEOUT); 73 if (bytes <= 0) { 74 return -1; 75 } 76 } 77 return len; 78 } 79 80 void _thread_exit(int id) { 81 PR_EnterMonitor(exit_cv); 82 #ifdef DEBUG 83 fprintf(stdout, "Thread %d EXIT\n", id); 84 #endif 85 86 _thread_exit_count--; 87 if (_thread_exit_count == 0) { 88 #ifdef DEBUG 89 fprintf(stdout, "Thread %d EXIT triggered notify\n", id); 90 #endif 91 PR_Notify(exit_cv); 92 } 93 PR_ExitMonitor(exit_cv); 94 } 95 96 void _server_thread(void* arg_id) { 97 void _client_thread(void*); 98 int* id = (int*)arg_id; 99 PRFileDesc* sock; 100 PRSocketOptionData sockopt; 101 PRNetAddr sa; 102 PRFileDesc* newsock; 103 char* data_buffer = NULL; 104 int data_buffer_size; 105 int index; 106 PRIntervalTime start, connect_done, read_done, write_done, close_done; 107 108 #ifdef DEBUG 109 fprintf(stdout, "server thread %d alive\n", *id); 110 #endif 111 112 data_buffer_size = 113 (_client_data > _server_data ? _client_data : _server_data); 114 115 if ((data_buffer = (char*)PR_Malloc(data_buffer_size * sizeof(char))) == 116 NULL) { 117 fprintf(stderr, "Error creating buffer in server thread %d\n", *id); 118 goto done; 119 } 120 121 if ((sock = PR_NewTCPSocket()) == NULL) { 122 fprintf(stderr, "Error creating socket in server thread %d\n", *id); 123 goto done; 124 } 125 126 sockopt.option = PR_SockOpt_Reuseaddr; 127 sockopt.value.reuse_addr = PR_TRUE; 128 if (PR_SetSocketOption(sock, &sockopt) == PR_FAILURE) { 129 fprintf(stderr, "Error setting socket option in server thread %d\n", *id); 130 goto done; 131 } 132 133 memset(&sa, 0, sizeof(sa)); 134 sa.inet.family = PR_AF_INET; 135 sa.inet.port = PR_htons(PORT_BASE + *id); 136 sa.inet.ip = PR_htonl(PR_INADDR_ANY); 137 138 if (PR_Bind(sock, &sa) < 0) { 139 fprintf(stderr, "Error binding socket in server thread %d errno = %d\n", 140 *id, errno); 141 goto done; 142 } 143 144 if (PR_Listen(sock, 32) < 0) { 145 fprintf(stderr, "Error listening to socket in server thread %d\n", *id); 146 goto done; 147 } 148 149 /* Tell the client to start */ 150 if (PR_CreateThread(PR_USER_THREAD, _client_thread, id, PR_PRIORITY_NORMAL, 151 scope2, PR_UNJOINABLE_THREAD, 0) == NULL) { 152 fprintf(stderr, "Error creating client thread %d\n", *id); 153 } 154 155 for (index = 0; index < _iterations; index++) { 156 #ifdef DEBUG 157 fprintf(stdout, "server thread %d loop %d\n", *id, index); 158 #endif 159 160 start = PR_IntervalNow(); 161 162 if ((newsock = PR_Accept(sock, &sa, PR_INTERVAL_NO_TIMEOUT)) == NULL) { 163 fprintf(stderr, "Error accepting connection %d in server thread %d\n", 164 index, *id); 165 goto done; 166 } 167 #ifdef DEBUG 168 fprintf(stdout, "server thread %d got connection %d\n", *id, newsock); 169 #endif 170 171 connect_done = PR_IntervalNow(); 172 173 if (_readn(newsock, data_buffer, _client_data) < _client_data) { 174 fprintf( 175 stderr, 176 "Error reading client data for iteration %d in server thread %d\n", 177 index, *id); 178 goto done; 179 } 180 181 #ifdef DEBUG 182 fprintf(stdout, "server thread %d read %d bytes\n", *id, _client_data); 183 #endif 184 read_done = PR_IntervalNow(); 185 186 if (PR_Send(newsock, data_buffer, _server_data, 0, PR_INTERVAL_NO_TIMEOUT) < 187 _server_data) { 188 fprintf( 189 stderr, 190 "Error sending client data for iteration %d in server thread %d\n", 191 index, *id); 192 goto done; 193 } 194 195 #ifdef DEBUG 196 fprintf(stdout, "server thread %d write %d bytes\n", *id, _server_data); 197 #endif 198 199 write_done = PR_IntervalNow(); 200 201 PR_Close(newsock); 202 203 close_done = PR_IntervalNow(); 204 205 timer_data[2 * (*id)].d_connect += _delta(&start, &connect_done); 206 timer_data[2 * (*id)].d_cl_data += _delta(&connect_done, &read_done); 207 timer_data[2 * (*id)].d_sv_data += _delta(&read_done, &write_done); 208 timer_data[2 * (*id)].d_close += _delta(&write_done, &close_done); 209 timer_data[2 * (*id)].d_total += _delta(&start, &close_done); 210 timer_data[2 * (*id)].requests++; 211 212 #ifdef DEBUG 213 fprintf(stdout, "server: %d %d %d %d %d\n", _delta(&start, &connect_done), 214 _delta(&connect_done, &read_done), _delta(&read_done, &write_done), 215 _delta(&write_done, &close_done), _delta(&start, &close_done)); 216 #endif 217 } 218 219 done: 220 if (data_buffer != NULL) { 221 PR_Free(data_buffer); 222 } 223 if (sock) { 224 PR_Close(sock); 225 } 226 _thread_exit(*id); 227 return; 228 } 229 230 void _client_thread(void* arg_id) { 231 int* id = (int*)arg_id; 232 int index; 233 PRNetAddr sa; 234 PRFileDesc* sock_h; 235 char* data_buffer = NULL; 236 int data_buffer_size; 237 int bytes; 238 PRIntervalTime start, connect_done, read_done, write_done, close_done; 239 PRStatus rv; 240 241 #ifdef DEBUG 242 fprintf(stdout, "client thread %d alive\n", *id); 243 #endif 244 245 data_buffer_size = 246 (_client_data > _server_data ? _client_data : _server_data); 247 248 if ((data_buffer = (char*)PR_Malloc(data_buffer_size * sizeof(char))) == 249 NULL) { 250 fprintf(stderr, "Error creating buffer in server thread %d\n", *id); 251 goto done; 252 } 253 254 memset(&sa, 0, sizeof(sa)); 255 rv = PR_InitializeNetAddr(PR_IpAddrLoopback, PORT_BASE + *id, &sa); 256 PR_ASSERT(PR_SUCCESS == rv); 257 258 for (index = 0; index < _iterations; index++) { 259 #ifdef DEBUG 260 fprintf(stdout, "client thread %d loop %d\n", *id, index); 261 #endif 262 263 start = PR_IntervalNow(); 264 if ((sock_h = PR_NewTCPSocket()) == NULL) { 265 fprintf(stderr, "Error creating socket %d in client thread %d\n", index, 266 *id); 267 goto done; 268 } 269 270 #ifdef DEBUG 271 fprintf(stdout, "client thread %d socket created %d\n", *id, sock_h); 272 #endif 273 274 if (PR_Connect(sock_h, &sa, PR_INTERVAL_NO_TIMEOUT) < 0) { 275 fprintf(stderr, "Error accepting connection %d in client thread %d\n", 276 index, *id); 277 goto done; 278 } 279 280 #ifdef DEBUG 281 fprintf(stdout, "client thread %d socket connected %d\n", *id, sock_h); 282 #endif 283 284 connect_done = PR_IntervalNow(); 285 if (PR_Send(sock_h, data_buffer, _client_data, 0, PR_INTERVAL_NO_TIMEOUT) < 286 _client_data) { 287 fprintf( 288 stderr, 289 "Error sending client data for iteration %d in client thread %d\n", 290 index, *id); 291 goto done; 292 } 293 294 #ifdef DEBUG 295 fprintf(stdout, "client thread %d socket wrote %d\n", *id, _client_data); 296 #endif 297 298 write_done = PR_IntervalNow(); 299 if ((bytes = _readn(sock_h, data_buffer, _server_data)) < _server_data) { 300 fprintf(stderr, 301 "Error reading server data for iteration %d in client thread %d " 302 "(read %d bytes)\n", 303 index, *id, bytes); 304 goto done; 305 } 306 307 #ifdef DEBUG 308 fprintf(stdout, "client thread %d socket read %d\n", *id, _server_data); 309 #endif 310 311 read_done = PR_IntervalNow(); 312 PR_Close(sock_h); 313 close_done = PR_IntervalNow(); 314 315 timer_data[2 * (*id) + 1].d_connect += _delta(&start, &connect_done); 316 timer_data[2 * (*id) + 1].d_cl_data += _delta(&connect_done, &write_done); 317 timer_data[2 * (*id) + 1].d_sv_data += _delta(&write_done, &read_done); 318 timer_data[2 * (*id) + 1].d_close += _delta(&read_done, &close_done); 319 timer_data[2 * (*id) + 1].d_total += _delta(&start, &close_done); 320 timer_data[2 * (*id) + 1].requests++; 321 } 322 done: 323 if (data_buffer != NULL) { 324 PR_Free(data_buffer); 325 } 326 _thread_exit(*id); 327 328 return; 329 } 330 331 static void do_work(void) { 332 int index; 333 334 _thread_exit_count = _threads * 2; 335 for (index = 0; index < _threads; index++) { 336 int* id = (int*)PR_Malloc(sizeof(int)); 337 338 *id = index; 339 340 if (PR_CreateThread(PR_USER_THREAD, _server_thread, id, PR_PRIORITY_NORMAL, 341 scope1, PR_UNJOINABLE_THREAD, 0) == NULL) { 342 fprintf(stderr, "Error creating server thread %d\n", index); 343 } 344 } 345 346 PR_EnterMonitor(exit_cv); 347 while (_thread_exit_count > 0) { 348 PR_Wait(exit_cv, PR_INTERVAL_NO_TIMEOUT); 349 } 350 PR_ExitMonitor(exit_cv); 351 352 fprintf(stdout, "TEST COMPLETE!\n"); 353 354 tally_results(verbose); 355 } 356 357 static void do_workUU(void) { 358 scope1 = PR_LOCAL_THREAD; 359 scope2 = PR_LOCAL_THREAD; 360 do_work(); 361 } 362 363 static void do_workUK(void) { 364 scope1 = PR_LOCAL_THREAD; 365 scope2 = PR_GLOBAL_THREAD; 366 do_work(); 367 } 368 369 static void do_workKU(void) { 370 scope1 = PR_GLOBAL_THREAD; 371 scope2 = PR_LOCAL_THREAD; 372 do_work(); 373 } 374 375 static void do_workKK(void) { 376 scope1 = PR_GLOBAL_THREAD; 377 scope2 = PR_GLOBAL_THREAD; 378 do_work(); 379 } 380 381 static void Measure(void (*func)(void), const char* msg) { 382 PRIntervalTime start, stop; 383 double d; 384 385 start = PR_IntervalNow(); 386 (*func)(); 387 stop = PR_IntervalNow(); 388 389 d = (double)PR_IntervalToMicroseconds(stop - start); 390 391 printf("%40s: %6.2f usec\n", msg, d / _iterations); 392 } 393 394 int main(int argc, char** argv) { 395 #if defined(XP_UNIX) 396 int opt; 397 PR_IMPORT_DATA(char*) optarg; 398 #endif 399 400 #if defined(XP_UNIX) 401 while ((opt = getopt(argc, argv, "c:s:i:t:v")) != EOF) { 402 switch (opt) { 403 case 'i': 404 _iterations = atoi(optarg); 405 break; 406 case 't': 407 _threads_max = _threads = atoi(optarg); 408 break; 409 case 'c': 410 _client_data = atoi(optarg); 411 break; 412 case 's': 413 _server_data = atoi(optarg); 414 break; 415 case 'v': 416 verbose = 1; 417 break; 418 default: 419 break; 420 } 421 } 422 #endif 423 424 PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); 425 426 fprintf(stdout, 427 "Running test for %d iterations with %d simultaneous threads.\n", 428 _iterations, _threads); 429 fprintf(stdout, 430 "\tWill send %d bytes of client data and %d bytes of server data\n", 431 _client_data, _server_data); 432 433 if ((exit_cv = PR_NewMonitor()) == NULL) { 434 fprintf(stderr, "Error creating monitor for exit cv\n"); 435 } 436 if ((timer_data = (timer_slot_t*)PR_Malloc(2 * _threads * 437 sizeof(timer_slot_t))) == NULL) { 438 fprintf(stderr, "error allocating thread time results array\n"); 439 } 440 memset(timer_data, 0, 2 * _threads * sizeof(timer_slot_t)); 441 442 Measure(do_workUU, "select loop user/user"); 443 Measure(do_workUK, "select loop user/kernel"); 444 Measure(do_workKU, "select loop kernel/user"); 445 Measure(do_workKK, "select loop kernel/kernel"); 446 447 return 0; 448 } 449 450 void tally_results(int verbose) { 451 int index; 452 unsigned long tot_connect = 0; 453 unsigned long tot_cl_data = 0; 454 unsigned long tot_sv_data = 0; 455 unsigned long tot_close = 0; 456 unsigned long tot_all = 0; 457 unsigned long tot_requests = 0; 458 459 fprintf(stdout, "Server results:\n\n"); 460 for (index = 0; index < _threads_max * 2; index += 2) { 461 if (verbose) 462 fprintf(stdout, "server thread %u\t%u\t%u\t%u\t%u\t%u\t%u\n", index, 463 timer_data[index].requests, timer_data[index].d_connect, 464 timer_data[index].d_cl_data, timer_data[index].d_sv_data, 465 timer_data[index].d_close, timer_data[index].d_total); 466 467 tot_connect += timer_data[index].d_connect / _threads; 468 tot_cl_data += timer_data[index].d_cl_data / _threads; 469 tot_sv_data += timer_data[index].d_sv_data / _threads; 470 tot_close += timer_data[index].d_close / _threads; 471 tot_all += timer_data[index].d_total / _threads; 472 tot_requests += timer_data[index].requests / _threads; 473 } 474 fprintf(stdout, "----------\n"); 475 fprintf(stdout, "server per thread totals %u\t%u\t%u\t%u\t%u\n", tot_requests, 476 tot_connect, tot_cl_data, tot_sv_data, tot_close); 477 fprintf(stdout, "server per thread elapsed time %u\n", tot_all); 478 fprintf(stdout, "----------\n"); 479 480 tot_connect = tot_cl_data = tot_sv_data = tot_close = tot_all = tot_requests = 481 0; 482 fprintf(stdout, "Client results:\n\n"); 483 for (index = 1; index < _threads_max * 2; index += 2) { 484 if (verbose) 485 fprintf(stdout, "client thread %u\t%u\t%u\t%u\t%u\t%u\t%u\n", index, 486 timer_data[index].requests, timer_data[index].d_connect, 487 timer_data[index].d_cl_data, timer_data[index].d_sv_data, 488 timer_data[index].d_close, timer_data[index].d_total); 489 490 tot_connect += timer_data[index].d_connect / _threads; 491 tot_cl_data += timer_data[index].d_cl_data / _threads; 492 tot_sv_data += timer_data[index].d_sv_data / _threads; 493 tot_close += timer_data[index].d_close / _threads; 494 tot_all += timer_data[index].d_total / _threads; 495 tot_requests += timer_data[index].requests / _threads; 496 } 497 fprintf(stdout, "----------\n"); 498 fprintf(stdout, "client per thread totals %u\t%u\t%u\t%u\t%u\n", tot_requests, 499 tot_connect, tot_cl_data, tot_sv_data, tot_close); 500 fprintf(stdout, "client per thread elapsed time %u\n", tot_all); 501 }