1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/nsprpub/pr/tests/sel_spd.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,523 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + * Test the speed of select within NSPR 1.11 + * 1.12 + */ 1.13 + 1.14 +#include "nspr.h" 1.15 +#include "prpriv.h" 1.16 + 1.17 +#include <stdlib.h> 1.18 +#include <stdio.h> 1.19 +#include <errno.h> 1.20 +#include <string.h> 1.21 +#ifdef SYMBIAN 1.22 +#include <getopt.h> 1.23 +#endif 1.24 + 1.25 +#define PORT_BASE 19000 1.26 + 1.27 +typedef struct timer_slot_t { 1.28 + unsigned long d_connect; 1.29 + unsigned long d_cl_data; 1.30 + unsigned long d_sv_data; 1.31 + unsigned long d_close; 1.32 + unsigned long d_total; 1.33 + unsigned long requests; 1.34 +} timer_slot_t; 1.35 + 1.36 +static long _iterations = 5; 1.37 +static long _client_data = 8192; 1.38 + 1.39 +#ifdef SYMBIAN 1.40 +/* 1.41 + * Symbian OS does not scale well specially the requirement for thread stack 1.42 + * space and buffer allocation space. It is easy to get into a fragmented 1.43 + * memory and not be able to allocate thread stack or client/server data 1.44 + * buffer. 1.45 + */ 1.46 +static long _server_data = (8*1024); 1.47 +static long _threads_max = 10, _threads = 10; 1.48 +#else 1.49 +static long _server_data = (128*1024); 1.50 +static long _threads_max = 10, _threads = 10; 1.51 +#endif 1.52 + 1.53 +static int verbose=0; 1.54 +static PRMonitor *exit_cv; 1.55 +static long _thread_exit_count; 1.56 +static timer_slot_t *timer_data; 1.57 +static PRThreadScope scope1, scope2; 1.58 + 1.59 +void tally_results(int); 1.60 + 1.61 +/* return the diff in microseconds */ 1.62 +unsigned long _delta(PRIntervalTime *start, PRIntervalTime *stop) 1.63 +{ 1.64 + /* 1.65 + * Will C do the right thing with unsigned arithemtic? 1.66 + */ 1.67 + return PR_IntervalToMicroseconds(*stop - *start); 1.68 +} 1.69 + 1.70 +int _readn(PRFileDesc *sock, char *buf, int len) 1.71 +{ 1.72 + int rem; 1.73 + int bytes; 1.74 + 1.75 + for (rem=len; rem; rem -= bytes) { 1.76 + bytes = PR_Recv(sock, buf+len-rem, rem, 0, PR_INTERVAL_NO_TIMEOUT); 1.77 + if (bytes <= 0) 1.78 + return -1; 1.79 + } 1.80 + return len; 1.81 +} 1.82 + 1.83 +void 1.84 +_thread_exit(int id) 1.85 +{ 1.86 + PR_EnterMonitor(exit_cv); 1.87 +#ifdef DEBUG 1.88 + fprintf(stdout, "Thread %d EXIT\n", id); 1.89 +#endif 1.90 + 1.91 + _thread_exit_count--; 1.92 + if (_thread_exit_count == 0) { 1.93 +#ifdef DEBUG 1.94 + fprintf(stdout, "Thread %d EXIT triggered notify\n", id); 1.95 +#endif 1.96 + PR_Notify(exit_cv); 1.97 + } 1.98 + PR_ExitMonitor(exit_cv); 1.99 +} 1.100 + 1.101 +void 1.102 +_server_thread(void *arg_id) 1.103 +{ 1.104 + void _client_thread(void *); 1.105 + PRThread *thread; 1.106 + int *id = (int *)arg_id; 1.107 + PRFileDesc *sock; 1.108 + PRSocketOptionData sockopt; 1.109 + PRNetAddr sa; 1.110 + PRFileDesc * newsock; 1.111 + char *data_buffer = NULL; 1.112 + int data_buffer_size; 1.113 + int index; 1.114 + PRIntervalTime start, 1.115 + connect_done, 1.116 + read_done, 1.117 + write_done, 1.118 + close_done; 1.119 + 1.120 + 1.121 +#ifdef DEBUG 1.122 + fprintf(stdout, "server thread %d alive\n", *id); 1.123 +#endif 1.124 + 1.125 + data_buffer_size = (_client_data>_server_data?_client_data:_server_data); 1.126 + 1.127 + if ( (data_buffer = (char *)PR_Malloc(data_buffer_size * sizeof(char))) == NULL ) { 1.128 + fprintf(stderr, "Error creating buffer in server thread %d\n", *id); 1.129 + goto done; 1.130 + } 1.131 + 1.132 + 1.133 + if ( (sock = PR_NewTCPSocket()) == NULL) { 1.134 + fprintf(stderr, "Error creating socket in server thread %d\n", *id); 1.135 + goto done; 1.136 + } 1.137 + 1.138 + sockopt.option = PR_SockOpt_Reuseaddr; 1.139 + sockopt.value.reuse_addr = PR_TRUE; 1.140 + if ( PR_SetSocketOption(sock, &sockopt) == PR_FAILURE) { 1.141 + fprintf(stderr, "Error setting socket option in server thread %d\n", *id); 1.142 + goto done; 1.143 + } 1.144 + 1.145 + memset(&sa, 0 , sizeof(sa)); 1.146 + sa.inet.family = PR_AF_INET; 1.147 + sa.inet.port = PR_htons(PORT_BASE + *id); 1.148 + sa.inet.ip = PR_htonl(PR_INADDR_ANY); 1.149 + 1.150 + if ( PR_Bind(sock, &sa) < 0) { 1.151 + fprintf(stderr, "Error binding socket in server thread %d errno = %d\n", *id, errno); 1.152 + goto done; 1.153 + } 1.154 + 1.155 + if ( PR_Listen(sock, 32) < 0 ) { 1.156 + fprintf(stderr, "Error listening to socket in server thread %d\n", *id); 1.157 + goto done; 1.158 + } 1.159 + 1.160 + /* Tell the client to start */ 1.161 + if ( (thread = PR_CreateThread(PR_USER_THREAD, 1.162 + _client_thread, 1.163 + id, 1.164 + PR_PRIORITY_NORMAL, 1.165 + scope2, 1.166 + PR_UNJOINABLE_THREAD, 1.167 + 0)) == NULL) 1.168 + fprintf(stderr, "Error creating client thread %d\n", *id); 1.169 + 1.170 + for (index = 0; index< _iterations; index++) { 1.171 + 1.172 +#ifdef DEBUG 1.173 + fprintf(stdout, "server thread %d loop %d\n", *id, index); 1.174 +#endif 1.175 + 1.176 + start = PR_IntervalNow(); 1.177 + 1.178 + if ( (newsock = PR_Accept(sock, &sa, 1.179 + PR_INTERVAL_NO_TIMEOUT)) == NULL) { 1.180 + fprintf(stderr, "Error accepting connection %d in server thread %d\n", 1.181 + index, *id); 1.182 + goto done; 1.183 + } 1.184 +#ifdef DEBUG 1.185 + fprintf(stdout, "server thread %d got connection %d\n", *id, newsock); 1.186 +#endif 1.187 + 1.188 + 1.189 + connect_done = PR_IntervalNow(); 1.190 + 1.191 + if ( _readn(newsock, data_buffer, _client_data) < _client_data) { 1.192 + fprintf(stderr, "Error reading client data for iteration %d in server thread %d\n", index, *id ); 1.193 + goto done; 1.194 + } 1.195 + 1.196 +#ifdef DEBUG 1.197 + fprintf(stdout, "server thread %d read %d bytes\n", *id, _client_data); 1.198 +#endif 1.199 + read_done = PR_IntervalNow(); 1.200 + 1.201 + if ( PR_Send(newsock, data_buffer, _server_data, 0, 1.202 + PR_INTERVAL_NO_TIMEOUT) < _server_data) { 1.203 + fprintf(stderr, "Error sending client data for iteration %d in server thread %d\n", index, *id ); 1.204 + goto done; 1.205 + } 1.206 + 1.207 +#ifdef DEBUG 1.208 + fprintf(stdout, "server thread %d write %d bytes\n", *id, _server_data); 1.209 +#endif 1.210 + 1.211 + write_done = PR_IntervalNow(); 1.212 + 1.213 + PR_Close(newsock); 1.214 + 1.215 + close_done = PR_IntervalNow(); 1.216 + 1.217 + timer_data[2*(*id)].d_connect += _delta(&start, &connect_done); 1.218 + timer_data[2*(*id)].d_cl_data += _delta(&connect_done, &read_done); 1.219 + timer_data[2*(*id)].d_sv_data += _delta(&read_done, &write_done); 1.220 + timer_data[2*(*id)].d_close += _delta(&write_done, &close_done); 1.221 + timer_data[2*(*id)].d_total += _delta(&start, &close_done); 1.222 + timer_data[2*(*id)].requests++; 1.223 + 1.224 + 1.225 +#ifdef DEBUG 1.226 + fprintf(stdout, "server: %d %d %d %d %d\n", 1.227 + _delta(&start, &connect_done), _delta(&connect_done, &read_done), 1.228 + _delta(&read_done, &write_done), _delta(&write_done, &close_done), 1.229 + _delta(&start, &close_done)); 1.230 +#endif 1.231 + } 1.232 + 1.233 +done: 1.234 + if (data_buffer != NULL) PR_Free (data_buffer); 1.235 + if (sock) PR_Close(sock); 1.236 + _thread_exit(*id); 1.237 + return; 1.238 +} 1.239 + 1.240 +void 1.241 +_client_thread(void *arg_id) 1.242 +{ 1.243 + int *id = (int *)arg_id; 1.244 + int index; 1.245 + PRNetAddr sa; 1.246 + PRFileDesc *sock_h; 1.247 + char *data_buffer = NULL; 1.248 + int data_buffer_size; 1.249 + int bytes; 1.250 + PRIntervalTime start, 1.251 + connect_done, 1.252 + read_done, 1.253 + write_done, 1.254 + close_done; 1.255 + PRStatus rv; 1.256 + 1.257 +#ifdef DEBUG 1.258 + fprintf(stdout, "client thread %d alive\n", *id); 1.259 +#endif 1.260 + 1.261 + data_buffer_size = (_client_data>_server_data?_client_data:_server_data); 1.262 + 1.263 + if ( (data_buffer = (char *)PR_Malloc(data_buffer_size * sizeof(char))) == NULL) { 1.264 + fprintf(stderr, "Error creating buffer in server thread %d\n", *id); 1.265 + goto done; 1.266 + } 1.267 + 1.268 + memset(&sa, 0 , sizeof(sa)); 1.269 + rv = PR_InitializeNetAddr(PR_IpAddrLoopback, PORT_BASE + *id, &sa); 1.270 + PR_ASSERT(PR_SUCCESS == rv); 1.271 + 1.272 + for (index = 0; index< _iterations; index++) { 1.273 + 1.274 +#ifdef DEBUG 1.275 + fprintf(stdout, "client thread %d loop %d\n", *id, index); 1.276 +#endif 1.277 + 1.278 + start = PR_IntervalNow(); 1.279 + if ( (sock_h = PR_NewTCPSocket()) == NULL) { 1.280 + fprintf(stderr, "Error creating socket %d in client thread %d\n", 1.281 + index, *id); 1.282 + goto done; 1.283 + } 1.284 + 1.285 +#ifdef DEBUG 1.286 + fprintf(stdout, "client thread %d socket created %d\n", *id, sock_h); 1.287 +#endif 1.288 + 1.289 + if ( PR_Connect(sock_h, &sa, 1.290 + PR_INTERVAL_NO_TIMEOUT) < 0) { 1.291 + fprintf(stderr, "Error accepting connection %d in client thread %d\n", 1.292 + index, *id); 1.293 + goto done; 1.294 + } 1.295 + 1.296 +#ifdef DEBUG 1.297 + fprintf(stdout, "client thread %d socket connected %d\n", *id, sock_h); 1.298 +#endif 1.299 + 1.300 + connect_done = PR_IntervalNow(); 1.301 + if ( PR_Send(sock_h, data_buffer, _client_data, 0, 1.302 + PR_INTERVAL_NO_TIMEOUT) < _client_data) { 1.303 + fprintf(stderr, "Error sending client data for iteration %d in client thread %d\n", index, *id ); 1.304 + goto done; 1.305 + } 1.306 + 1.307 +#ifdef DEBUG 1.308 + fprintf(stdout, "client thread %d socket wrote %d\n", *id, _client_data); 1.309 +#endif 1.310 + 1.311 + write_done = PR_IntervalNow(); 1.312 + if ( (bytes = _readn(sock_h, data_buffer, _server_data)) < _server_data) { 1.313 + fprintf(stderr, "Error reading server data for iteration %d in client thread %d (read %d bytes)\n", index, *id, bytes ); 1.314 + goto done; 1.315 + } 1.316 + 1.317 +#ifdef DEBUG 1.318 + fprintf(stdout, "client thread %d socket read %d\n", *id, _server_data); 1.319 +#endif 1.320 + 1.321 + read_done = PR_IntervalNow(); 1.322 + PR_Close(sock_h); 1.323 + close_done = PR_IntervalNow(); 1.324 + 1.325 + timer_data[2*(*id)+1].d_connect += _delta(&start, &connect_done); 1.326 + timer_data[2*(*id)+1].d_cl_data += _delta(&connect_done, &write_done); 1.327 + timer_data[2*(*id)+1].d_sv_data += _delta(&write_done, &read_done); 1.328 + timer_data[2*(*id)+1].d_close += _delta(&read_done, &close_done); 1.329 + timer_data[2*(*id)+1].d_total += _delta(&start, &close_done); 1.330 + timer_data[2*(*id)+1].requests++; 1.331 + } 1.332 +done: 1.333 + if (data_buffer != NULL) PR_Free (data_buffer); 1.334 + _thread_exit(*id); 1.335 + 1.336 + return; 1.337 +} 1.338 + 1.339 +static 1.340 +void do_work(void) 1.341 +{ 1.342 + int index; 1.343 + 1.344 + _thread_exit_count = _threads * 2; 1.345 + for (index=0; index<_threads; index++) { 1.346 + PRThread *thread; 1.347 + int *id = (int *)PR_Malloc(sizeof(int)); 1.348 + 1.349 + *id = index; 1.350 + 1.351 + if ( (thread = PR_CreateThread(PR_USER_THREAD, 1.352 + _server_thread, 1.353 + id, 1.354 + PR_PRIORITY_NORMAL, 1.355 + scope1, 1.356 + PR_UNJOINABLE_THREAD, 1.357 + 0)) == NULL) 1.358 + fprintf(stderr, "Error creating server thread %d\n", index); 1.359 + } 1.360 + 1.361 + PR_EnterMonitor(exit_cv); 1.362 + while (_thread_exit_count > 0) 1.363 + PR_Wait(exit_cv, PR_INTERVAL_NO_TIMEOUT); 1.364 + PR_ExitMonitor(exit_cv); 1.365 + 1.366 + fprintf(stdout, "TEST COMPLETE!\n"); 1.367 + 1.368 + tally_results(verbose); 1.369 + 1.370 +} 1.371 + 1.372 +static void do_workUU(void) 1.373 +{ 1.374 + scope1 = PR_LOCAL_THREAD; 1.375 + scope2 = PR_LOCAL_THREAD; 1.376 + do_work(); 1.377 +} 1.378 + 1.379 +static void do_workUK(void) 1.380 +{ 1.381 + scope1 = PR_LOCAL_THREAD; 1.382 + scope2 = PR_GLOBAL_THREAD; 1.383 + do_work(); 1.384 +} 1.385 + 1.386 +static void do_workKU(void) 1.387 +{ 1.388 + scope1 = PR_GLOBAL_THREAD; 1.389 + scope2 = PR_LOCAL_THREAD; 1.390 + do_work(); 1.391 +} 1.392 + 1.393 +static void do_workKK(void) 1.394 +{ 1.395 + scope1 = PR_GLOBAL_THREAD; 1.396 + scope2 = PR_GLOBAL_THREAD; 1.397 + do_work(); 1.398 +} 1.399 + 1.400 + 1.401 + 1.402 +static void Measure(void (*func)(void), const char *msg) 1.403 +{ 1.404 + PRIntervalTime start, stop; 1.405 + double d; 1.406 + 1.407 + start = PR_IntervalNow(); 1.408 + (*func)(); 1.409 + stop = PR_IntervalNow(); 1.410 + 1.411 + d = (double)PR_IntervalToMicroseconds(stop - start); 1.412 + 1.413 + printf("%40s: %6.2f usec\n", msg, d / _iterations); 1.414 +} 1.415 + 1.416 + 1.417 +int main(int argc, char **argv) 1.418 +{ 1.419 +#if defined(XP_UNIX) || defined(XP_OS2) 1.420 + int opt; 1.421 + PR_IMPORT_DATA(char *) optarg; 1.422 +#endif 1.423 + 1.424 +#if defined(XP_UNIX) || defined(XP_OS2) 1.425 + while ( (opt = getopt(argc, argv, "c:s:i:t:v")) != EOF) { 1.426 + switch(opt) { 1.427 + case 'i': 1.428 + _iterations = atoi(optarg); 1.429 + break; 1.430 + case 't': 1.431 + _threads_max = _threads = atoi(optarg); 1.432 + break; 1.433 + case 'c': 1.434 + _client_data = atoi(optarg); 1.435 + break; 1.436 + case 's': 1.437 + _server_data = atoi(optarg); 1.438 + break; 1.439 + case 'v': 1.440 + verbose = 1; 1.441 + break; 1.442 + default: 1.443 + break; 1.444 + } 1.445 + } 1.446 +#endif 1.447 + 1.448 + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); 1.449 + PR_STDIO_INIT(); 1.450 + 1.451 + fprintf(stdout, "Running test for %d iterations with %d simultaneous threads.\n", 1.452 + _iterations, _threads); 1.453 + fprintf(stdout, "\tWill send %d bytes of client data and %d bytes of server data\n", 1.454 + _client_data, _server_data); 1.455 + 1.456 + if ( (exit_cv = PR_NewMonitor()) == NULL) 1.457 + fprintf(stderr, "Error creating monitor for exit cv\n"); 1.458 + if ( (timer_data = (timer_slot_t *)PR_Malloc(2*_threads * sizeof(timer_slot_t))) == NULL) 1.459 + fprintf(stderr, "error allocating thread time results array\n"); 1.460 + memset(timer_data, 0 , 2*_threads*sizeof(timer_slot_t)); 1.461 + 1.462 + Measure(do_workUU, "select loop user/user"); 1.463 + Measure(do_workUK, "select loop user/kernel"); 1.464 + Measure(do_workKU, "select loop kernel/user"); 1.465 + Measure(do_workKK, "select loop kernel/kernel"); 1.466 + 1.467 + 1.468 + return 0; 1.469 +} 1.470 + 1.471 +void 1.472 +tally_results(int verbose) 1.473 +{ 1.474 + int index; 1.475 + unsigned long tot_connect = 0; 1.476 + unsigned long tot_cl_data = 0; 1.477 + unsigned long tot_sv_data = 0; 1.478 + unsigned long tot_close = 0; 1.479 + unsigned long tot_all = 0; 1.480 + unsigned long tot_requests = 0; 1.481 + 1.482 + fprintf(stdout, "Server results:\n\n"); 1.483 + for (index=0; index<_threads_max*2; index+=2) { 1.484 + 1.485 + if (verbose) 1.486 + fprintf(stdout, "server thread %u\t%u\t%u\t%u\t%u\t%u\t%u\n", 1.487 + index, timer_data[index].requests, timer_data[index].d_connect, 1.488 + timer_data[index].d_cl_data, timer_data[index].d_sv_data, 1.489 + timer_data[index].d_close, timer_data[index].d_total); 1.490 + 1.491 + tot_connect += timer_data[index].d_connect / _threads; 1.492 + tot_cl_data += timer_data[index].d_cl_data / _threads; 1.493 + tot_sv_data += timer_data[index].d_sv_data / _threads; 1.494 + tot_close += timer_data[index].d_close / _threads; 1.495 + tot_all += timer_data[index].d_total / _threads; 1.496 + tot_requests += timer_data[index].requests / _threads; 1.497 + } 1.498 + fprintf(stdout, "----------\n"); 1.499 + fprintf(stdout, "server per thread totals %u\t%u\t%u\t%u\t%u\n", 1.500 + tot_requests, tot_connect, tot_cl_data, tot_sv_data, tot_close); 1.501 + fprintf(stdout, "server per thread elapsed time %u\n", tot_all); 1.502 + fprintf(stdout, "----------\n"); 1.503 + 1.504 + tot_connect = tot_cl_data = tot_sv_data = tot_close = tot_all = tot_requests = 0; 1.505 + fprintf(stdout, "Client results:\n\n"); 1.506 + for (index=1; index<_threads_max*2; index+=2) { 1.507 + 1.508 + if (verbose) 1.509 + fprintf(stdout, "client thread %u\t%u\t%u\t%u\t%u\t%u\t%u\n", 1.510 + index, timer_data[index].requests, timer_data[index].d_connect, 1.511 + timer_data[index].d_cl_data, timer_data[index].d_sv_data, 1.512 + timer_data[index].d_close, timer_data[index].d_total); 1.513 + 1.514 + tot_connect += timer_data[index].d_connect / _threads; 1.515 + tot_cl_data += timer_data[index].d_cl_data / _threads; 1.516 + tot_sv_data += timer_data[index].d_sv_data / _threads; 1.517 + tot_close += timer_data[index].d_close / _threads; 1.518 + tot_all += timer_data[index].d_total / _threads; 1.519 + tot_requests += timer_data[index].requests / _threads; 1.520 + } 1.521 + fprintf(stdout, "----------\n"); 1.522 + fprintf(stdout, "client per thread totals %u\t%u\t%u\t%u\t%u\n", 1.523 + tot_requests, tot_connect, tot_cl_data, tot_sv_data, tot_close); 1.524 + fprintf(stdout, "client per thread elapsed time %u\n", tot_all); 1.525 +} 1.526 +