nsprpub/pr/tests/sel_spd.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     6 /*
     7  * Test the speed of select within NSPR
     8  *
     9  */
    11 #include "nspr.h"
    12 #include "prpriv.h"
    14 #include <stdlib.h>
    15 #include <stdio.h>
    16 #include <errno.h>
    17 #include <string.h>
    18 #ifdef SYMBIAN
    19 #include <getopt.h>
    20 #endif
    22 #define PORT_BASE 19000
    24 typedef struct timer_slot_t {
    25 	unsigned long d_connect;
    26 	unsigned long d_cl_data;
    27 	unsigned long d_sv_data;
    28 	unsigned long d_close;
    29 	unsigned long d_total;
    30 	unsigned long requests;
    31 } timer_slot_t;
    33 static long _iterations = 5;
    34 static long _client_data = 8192;
    36 #ifdef SYMBIAN
    37 /*
    38  * Symbian OS does not scale well specially the requirement for thread stack
    39  * space and buffer allocation space.  It is easy to get into a fragmented
    40  * memory and not be able to allocate thread stack or client/server data
    41  * buffer.
    42  */
    43 static long _server_data = (8*1024);
    44 static long _threads_max = 10, _threads = 10;
    45 #else
    46 static long _server_data = (128*1024);
    47 static long _threads_max = 10, _threads = 10;
    48 #endif
    50 static int verbose=0;
    51 static PRMonitor *exit_cv;
    52 static long _thread_exit_count;
    53 static timer_slot_t *timer_data;
    54 static PRThreadScope scope1, scope2;
    56 void tally_results(int);
    58 /* return the diff in microseconds */
    59 unsigned long _delta(PRIntervalTime *start, PRIntervalTime *stop)
    60 {
    61 	/*
    62 	 * Will C do the right thing with unsigned arithemtic?
    63 	 */
    64 	return PR_IntervalToMicroseconds(*stop - *start);
    65 }
    67 int _readn(PRFileDesc *sock, char *buf, int len)
    68 {
    69 	int rem;
    70 	int bytes;
    72 	for (rem=len; rem; rem -= bytes) {
    73 		bytes = PR_Recv(sock, buf+len-rem, rem, 0, PR_INTERVAL_NO_TIMEOUT);
    74 		if (bytes <= 0)
    75             return -1;
    76 	}
    77 	return len;
    78 }
    80 void
    81 _thread_exit(int id)
    82 {
    83 	PR_EnterMonitor(exit_cv);
    84 #ifdef DEBUG
    85 	fprintf(stdout, "Thread %d EXIT\n", id);
    86 #endif
    88 	_thread_exit_count--;
    89 	if (_thread_exit_count == 0) {
    90 #ifdef DEBUG
    91 	fprintf(stdout, "Thread %d EXIT triggered notify\n", id);
    92 #endif
    93 		PR_Notify(exit_cv);
    94 	}
    95 	PR_ExitMonitor(exit_cv);
    96 }
    98 void
    99 _server_thread(void *arg_id)
   100 {
   101 	void _client_thread(void *);
   102 	PRThread *thread;
   103 	int *id =  (int *)arg_id;
   104 	PRFileDesc *sock;
   105 	PRSocketOptionData sockopt;
   106 	PRNetAddr sa;
   107 	PRFileDesc * newsock;
   108 	char *data_buffer = NULL;
   109 	int data_buffer_size;
   110 	int index;
   111 	PRIntervalTime start,
   112 	      connect_done,
   113 	      read_done,
   114 	      write_done,
   115 	      close_done;
   118 #ifdef DEBUG
   119 	fprintf(stdout, "server thread %d alive\n", *id);
   120 #endif
   122 	data_buffer_size = (_client_data>_server_data?_client_data:_server_data);
   124 	if ( (data_buffer = (char *)PR_Malloc(data_buffer_size * sizeof(char))) == NULL ) {
   125 		fprintf(stderr, "Error creating buffer in server thread %d\n", *id);
   126 		goto done;
   127 	}
   130 	if ( (sock = PR_NewTCPSocket()) == NULL) {
   131 		fprintf(stderr, "Error creating socket in server thread %d\n", *id);
   132 		goto done;
   133 	}
   135 	sockopt.option = PR_SockOpt_Reuseaddr;
   136 	sockopt.value.reuse_addr = PR_TRUE;
   137 	if ( PR_SetSocketOption(sock, &sockopt) == PR_FAILURE) {
   138 		fprintf(stderr, "Error setting socket option in server thread %d\n", *id);
   139 		goto done;
   140 	}
   142 	memset(&sa, 0 , sizeof(sa));
   143 	sa.inet.family = PR_AF_INET;
   144 	sa.inet.port = PR_htons(PORT_BASE + *id);
   145 	sa.inet.ip = PR_htonl(PR_INADDR_ANY);
   147 	if ( PR_Bind(sock, &sa) < 0) {
   148 		fprintf(stderr, "Error binding socket in server thread %d errno = %d\n", *id, errno);
   149 		goto done;
   150 	}
   152 	if ( PR_Listen(sock, 32) < 0 ) {
   153 		fprintf(stderr, "Error listening to socket in server thread %d\n", *id);
   154 		goto done;
   155 	}
   157 	/* Tell the client to start */
   158 	if ( (thread = PR_CreateThread(PR_USER_THREAD, 
   159                                       _client_thread, 
   160                                       id, 
   161                                       PR_PRIORITY_NORMAL, 
   162                                       scope2, 
   163                                       PR_UNJOINABLE_THREAD, 
   164                                       0)) == NULL)
   165 		fprintf(stderr, "Error creating client thread %d\n", *id);
   167 	for (index = 0; index< _iterations; index++) {
   169 #ifdef DEBUG
   170 	fprintf(stdout, "server thread %d loop %d\n", *id, index);
   171 #endif
   173 		start = PR_IntervalNow();
   175 		if ( (newsock = PR_Accept(sock, &sa,
   176 					PR_INTERVAL_NO_TIMEOUT)) == NULL) {
   177 			fprintf(stderr, "Error accepting connection %d in server thread %d\n",
   178 				index, *id);
   179 			goto done;
   180 		}
   181 #ifdef DEBUG
   182 	fprintf(stdout, "server thread %d got connection %d\n", *id, newsock);
   183 #endif
   186 		connect_done = PR_IntervalNow();
   188 		if ( _readn(newsock, data_buffer, _client_data) < _client_data) {
   189 			fprintf(stderr, "Error reading client data for iteration %d in server thread %d\n", index, *id );
   190 			goto done;
   191 		}
   193 #ifdef DEBUG
   194 	fprintf(stdout, "server thread %d read %d bytes\n", *id, _client_data);
   195 #endif
   196 		read_done = PR_IntervalNow();
   198 		if ( PR_Send(newsock, data_buffer, _server_data, 0,
   199 				PR_INTERVAL_NO_TIMEOUT) < _server_data) {
   200 			fprintf(stderr, "Error sending client data for iteration %d in server thread %d\n", index, *id );
   201 			goto done;
   202 		}
   204 #ifdef DEBUG
   205 	fprintf(stdout, "server thread %d write %d bytes\n", *id, _server_data);
   206 #endif
   208 		write_done = PR_IntervalNow();
   210 		PR_Close(newsock);
   212 		close_done = PR_IntervalNow();
   214 		timer_data[2*(*id)].d_connect += _delta(&start, &connect_done);
   215 		timer_data[2*(*id)].d_cl_data += _delta(&connect_done, &read_done);
   216 		timer_data[2*(*id)].d_sv_data += _delta(&read_done, &write_done);
   217 		timer_data[2*(*id)].d_close += _delta(&write_done, &close_done);
   218 		timer_data[2*(*id)].d_total += _delta(&start, &close_done);
   219 		timer_data[2*(*id)].requests++;
   222 #ifdef DEBUG
   223 		fprintf(stdout, "server: %d %d %d %d %d\n",
   224 			 _delta(&start, &connect_done), _delta(&connect_done, &read_done),
   225 			 _delta(&read_done, &write_done), _delta(&write_done, &close_done),
   226 			_delta(&start, &close_done));
   227 #endif
   228 	}
   230 done:
   231 	if (data_buffer != NULL) PR_Free (data_buffer);
   232 	if (sock) PR_Close(sock);
   233 	_thread_exit(*id);
   234 	return;
   235 }
   237 void
   238 _client_thread(void *arg_id)
   239 {
   240 	int *id =  (int *)arg_id;
   241 	int index;
   242 	PRNetAddr sa;
   243 	PRFileDesc *sock_h;
   244 	char *data_buffer = NULL;
   245 	int data_buffer_size;
   246 	int bytes;
   247 	PRIntervalTime start,
   248 	      connect_done,
   249 	      read_done,
   250 	      write_done,
   251 	      close_done;
   252 	PRStatus rv;
   254 #ifdef DEBUG
   255 	fprintf(stdout, "client thread %d alive\n", *id);
   256 #endif
   258 	data_buffer_size = (_client_data>_server_data?_client_data:_server_data);
   260 	if ( (data_buffer = (char *)PR_Malloc(data_buffer_size * sizeof(char))) == NULL) {
   261 		fprintf(stderr, "Error creating buffer in server thread %d\n", *id);
   262 		goto done;
   263 	}
   265 	memset(&sa, 0 , sizeof(sa));
   266 	rv = PR_InitializeNetAddr(PR_IpAddrLoopback, PORT_BASE + *id, &sa);
   267 	PR_ASSERT(PR_SUCCESS == rv);
   269 	for (index = 0; index< _iterations; index++) {
   271 #ifdef DEBUG
   272 	fprintf(stdout, "client thread %d loop %d\n", *id, index);
   273 #endif
   275 		start = PR_IntervalNow();
   276 		if ( (sock_h = PR_NewTCPSocket()) == NULL) {
   277 			fprintf(stderr, "Error creating socket %d in client thread %d\n",
   278 				index, *id);
   279 			goto done;
   280 		}
   282 #ifdef DEBUG
   283 	fprintf(stdout, "client thread %d socket created %d\n", *id, sock_h);
   284 #endif
   286 		if ( PR_Connect(sock_h, &sa,
   287 				PR_INTERVAL_NO_TIMEOUT) < 0) {
   288 			fprintf(stderr, "Error accepting connection %d in client thread %d\n",
   289 				index, *id);
   290 			goto done;
   291 		}
   293 #ifdef DEBUG
   294 	fprintf(stdout, "client thread %d socket connected %d\n", *id, sock_h);
   295 #endif
   297 		connect_done = PR_IntervalNow();
   298 		if ( PR_Send(sock_h, data_buffer, _client_data, 0,
   299 				PR_INTERVAL_NO_TIMEOUT) < _client_data) {
   300 			fprintf(stderr, "Error sending client data for iteration %d in client thread %d\n", index, *id );
   301 			goto done;
   302 		}
   304 #ifdef DEBUG
   305 	fprintf(stdout, "client thread %d socket wrote %d\n", *id, _client_data);
   306 #endif
   308 		write_done = PR_IntervalNow();
   309 		if ( (bytes = _readn(sock_h, data_buffer, _server_data)) < _server_data) {
   310 			fprintf(stderr, "Error reading server data for iteration %d in client thread %d (read %d bytes)\n", index, *id, bytes );
   311 			goto done;
   312 		}
   314 #ifdef DEBUG
   315 	fprintf(stdout, "client thread %d socket read %d\n", *id, _server_data);
   316 #endif
   318 		read_done = PR_IntervalNow();
   319 		PR_Close(sock_h);
   320 		close_done = PR_IntervalNow();
   322 		timer_data[2*(*id)+1].d_connect += _delta(&start, &connect_done);
   323 		timer_data[2*(*id)+1].d_cl_data += _delta(&connect_done, &write_done);
   324 		timer_data[2*(*id)+1].d_sv_data += _delta(&write_done, &read_done);
   325 		timer_data[2*(*id)+1].d_close += _delta(&read_done, &close_done);
   326 		timer_data[2*(*id)+1].d_total += _delta(&start, &close_done);
   327 		timer_data[2*(*id)+1].requests++;
   328 	}
   329 done:
   330 	if (data_buffer != NULL) PR_Free (data_buffer);
   331 	_thread_exit(*id);
   333 	return;
   334 }
   336 static
   337 void do_work(void)
   338 {
   339     int index;
   341 	_thread_exit_count = _threads * 2;
   342 	for (index=0; index<_threads; index++) {
   343 		PRThread *thread;
   344 		int *id = (int *)PR_Malloc(sizeof(int));
   346 		*id = index;
   348 		if ( (thread = PR_CreateThread(PR_USER_THREAD, 
   349                                        _server_thread, 
   350                                        id, 
   351                                        PR_PRIORITY_NORMAL, 
   352                                        scope1, 
   353                                        PR_UNJOINABLE_THREAD, 
   354                                        0)) == NULL)
   355 			fprintf(stderr, "Error creating server thread %d\n", index);
   356 	}
   358 	PR_EnterMonitor(exit_cv);
   359 	while (_thread_exit_count > 0)
   360 		PR_Wait(exit_cv, PR_INTERVAL_NO_TIMEOUT);
   361 	PR_ExitMonitor(exit_cv);
   363 	fprintf(stdout, "TEST COMPLETE!\n");
   365 	tally_results(verbose);
   367 }
   369 static void do_workUU(void)
   370 {
   371     scope1 = PR_LOCAL_THREAD;
   372     scope2 = PR_LOCAL_THREAD;
   373     do_work();
   374 }
   376 static void do_workUK(void)
   377 {
   378     scope1 = PR_LOCAL_THREAD;
   379     scope2 = PR_GLOBAL_THREAD;
   380     do_work();
   381 }
   383 static void do_workKU(void)
   384 {
   385     scope1 = PR_GLOBAL_THREAD;
   386     scope2 = PR_LOCAL_THREAD;
   387     do_work();
   388 }
   390 static void do_workKK(void)
   391 {
   392     scope1 = PR_GLOBAL_THREAD;
   393     scope2 = PR_GLOBAL_THREAD;
   394     do_work();
   395 }
   399 static void Measure(void (*func)(void), const char *msg)
   400 {
   401     PRIntervalTime start, stop;
   402     double d;
   404     start = PR_IntervalNow();
   405     (*func)();
   406     stop = PR_IntervalNow();
   408     d = (double)PR_IntervalToMicroseconds(stop - start);
   410     printf("%40s: %6.2f usec\n", msg, d / _iterations);
   411 }
   414 int main(int argc, char **argv)
   415 {
   416 #if defined(XP_UNIX) || defined(XP_OS2)
   417 	int opt;
   418 	PR_IMPORT_DATA(char *) optarg;
   419 #endif
   421 #if defined(XP_UNIX) || defined(XP_OS2)
   422 	while ( (opt = getopt(argc, argv, "c:s:i:t:v")) != EOF) {
   423 		switch(opt) {
   424 			case 'i':
   425 				_iterations = atoi(optarg);
   426 				break;
   427 			case 't':
   428 				_threads_max = _threads = atoi(optarg);
   429 				break;
   430 			case 'c':
   431 				_client_data = atoi(optarg);
   432 				break;
   433 			case 's':
   434 				_server_data = atoi(optarg);
   435 				break;
   436 			case 'v':
   437 				verbose = 1;
   438 				break;
   439 			default: 
   440 				break;
   441 		}
   442 	}
   443 #endif
   445 	PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
   446     PR_STDIO_INIT();
   448 	fprintf(stdout, "Running test for %d iterations with %d simultaneous threads.\n", 
   449 		_iterations, _threads);
   450 	fprintf(stdout, "\tWill send %d bytes of client data and %d bytes of server data\n", 
   451 		_client_data, _server_data);
   453 	if ( (exit_cv = PR_NewMonitor()) == NULL) 
   454 		fprintf(stderr, "Error creating monitor for exit cv\n");
   455 	if ( (timer_data = (timer_slot_t *)PR_Malloc(2*_threads * sizeof(timer_slot_t))) == NULL) 
   456 		fprintf(stderr, "error allocating thread time results array\n");
   457 	memset(timer_data, 0 , 2*_threads*sizeof(timer_slot_t));
   459     Measure(do_workUU, "select loop user/user");
   460     Measure(do_workUK, "select loop user/kernel");
   461     Measure(do_workKU, "select loop kernel/user");
   462     Measure(do_workKK, "select loop kernel/kernel");
   465 	return 0;
   466 }
   468 void
   469 tally_results(int verbose)
   470 {
   471 	int index;
   472 	unsigned long tot_connect = 0;
   473 	unsigned long tot_cl_data = 0;
   474 	unsigned long tot_sv_data = 0;
   475 	unsigned long tot_close = 0;
   476 	unsigned long tot_all = 0;
   477 	unsigned long tot_requests = 0;
   479 	fprintf(stdout, "Server results:\n\n");
   480 	for (index=0; index<_threads_max*2; index+=2) {
   482 		if (verbose)
   483 			fprintf(stdout, "server thread %u\t%u\t%u\t%u\t%u\t%u\t%u\n",
   484 				index, timer_data[index].requests, timer_data[index].d_connect,
   485 				timer_data[index].d_cl_data, timer_data[index].d_sv_data,
   486 				timer_data[index].d_close, timer_data[index].d_total);
   488 		tot_connect += timer_data[index].d_connect / _threads;
   489 		tot_cl_data += timer_data[index].d_cl_data / _threads;
   490 		tot_sv_data += timer_data[index].d_sv_data / _threads;
   491 		tot_close += timer_data[index].d_close / _threads;
   492 		tot_all += timer_data[index].d_total / _threads;
   493 		tot_requests += timer_data[index].requests / _threads;
   494 	}
   495 	fprintf(stdout, "----------\n");
   496 	fprintf(stdout, "server per thread totals %u\t%u\t%u\t%u\t%u\n",
   497 		tot_requests, tot_connect, tot_cl_data, tot_sv_data, tot_close);
   498 	fprintf(stdout, "server per thread elapsed time %u\n", tot_all);
   499 	fprintf(stdout, "----------\n");
   501 	tot_connect = tot_cl_data = tot_sv_data = tot_close = tot_all = tot_requests = 0;
   502 	fprintf(stdout, "Client results:\n\n");
   503 	for (index=1; index<_threads_max*2; index+=2) {
   505 		if (verbose)
   506 			fprintf(stdout, "client thread %u\t%u\t%u\t%u\t%u\t%u\t%u\n",
   507 				index, timer_data[index].requests, timer_data[index].d_connect,
   508 				timer_data[index].d_cl_data, timer_data[index].d_sv_data,
   509 				timer_data[index].d_close, timer_data[index].d_total);
   511 		tot_connect += timer_data[index].d_connect / _threads;
   512 		tot_cl_data += timer_data[index].d_cl_data / _threads;
   513 		tot_sv_data += timer_data[index].d_sv_data / _threads;
   514 		tot_close += timer_data[index].d_close / _threads;
   515 		tot_all += timer_data[index].d_total / _threads;
   516 		tot_requests += timer_data[index].requests / _threads;
   517 	}
   518 	fprintf(stdout, "----------\n");
   519 	fprintf(stdout, "client per thread totals %u\t%u\t%u\t%u\t%u\n",
   520 		tot_requests, tot_connect, tot_cl_data, tot_sv_data, tot_close);
   521 	fprintf(stdout, "client per thread elapsed time %u\n", tot_all);
   522 }

mercurial