home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / nsprpub / pr / tests / sel_spd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  14.5 KB  |  548 lines

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