From: hadley@thoth.ics.uci.edu (Tedd Hadley)
Subject: SERVER OPTIMIZATION (too good to be true?)
Newsgroups: rec.games.netrek
Organization: UC Irvine Department of ICS
Date: 15 Dec 92 04:10:12 GMT

   (The following relates to slight change to the server (input.c) that
    greatly reduces the load put on the server host machine.)

   First, the results:

   Server code:	INL3.0
   machine:	sparc 1 (4/60, 12Mb ram)
   players:	16 (IBL robots) at 5 updates/s

   Before the change, the load average checked once per minute:

0.46 2.12 5.59 5.54 5.16 5.14 6.91 7.68 7.43 7.33 7.78 5.52 5.46 4.89 5.19
5.52 7.13 7.68 6.76 6.39 5.73 5.64 4.95 5.31 6.03 6.09 ...
   averaging 5.8 over 30 minutes (sparc 1 is the slowest sparc)

   After the change:

0.37 0.53 0.44 1.46 1.81 2.00 1.49 1.28 0.57 0.43 0.32 0.14 0.22 0.30 0.46
1.65 1.53 1.22 1.44 0.81 0.38 1.17 0.82 1.14 1.20 1.81  ...
   averaging 0.9 over 30 minutes.

   Server code:	INL3.0
   machine:	sparc 2 (4/75, 20Mb ram)
   players:	16 (IBL robots) at 5 updates/s

   Before the change:

0.75 1.36 1.88 3.47 3.48 3.64 4.34 3.90 2.79 3.43 3.03 2.31 2.36 3.59 3.92
4.80 4.83 4.36 3.20 2.91 3.32 1.93 1.91 3.43 2.04 1.62 ...
   averaging 2.9 over 30 minutes.

   After the change:

0.27 0.21 0.14 0.14 0.04 0.79 1.02 0.70 0.39 0.13 0.19 0.59 0.34 0.11 0.22
0.13 0.96 0.33 0.26 0.52 0.21 0.06 0.27 0.12 0.03 0.57 ...
   averaging 0.3 over 30 minutes.

   So what the hell's the change?  Well, all I did was replace the
   50,000 ms (pseudo-polling) loop in input.c with one that cycles at
   timerDelay (200,000 ms at 5 update/s), but stills catches user input
   as it's available.  The result seems to be at least a factor of 4
   improvement in running efficiency, and with no change apparent to
   the client (other then lack of server lag).

   Since I'm taking off for Christmas vacation in a couple days I thought
   I'd post the fix now for anyone who's interested.  The only change is
   to input.c and the file I'm appending should plug in directly to the
   INL, bronco, or UDP sources (no variables required).  I've been running
   the fix on vlsi.ics.uci.edu for the last few days .. everything seems
   to be fine.  Give it a try if you want to drop your server machine's load
   average by a few points (note: I don't know yet if DEC workstations
   will show the same improvement).

   Disclaimer: as with any new change, I can't guarantee that there isn't
   some other bug hidden somewhere else, so keep a backup handy :)


#include "copyright.h"

#include <stdio.h>
#include <math.h>
#include <sys/types.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include "defs.h"
#include "struct.h"
#include "data.h"

static int sendflag=0;
static int do_update=1;

setflag()
{
    if(do_update)
       intrupt();
    else
       sendflag=1;

    /* statements below are executed once per timerDelay */

    /* note that these two statements were originally expected to
       be checked once per 50000 (rather then once per timerDelay),
       so we duplicate the check after each readFromClient, below */

    if (me->p_updates > delay) {
	 me->p_flags &= ~(PFWAR);
    }
    if (me->p_updates > rdelay) {
	 me->p_flags &= ~(PFREFITTING);
    }
     /* Keep self from being nuked... */
    me->p_ghostbuster = 0;
}

input()
{
    int fd, nchar;
    struct itimerval	udt;
    struct timeval timeout;
    fd_set readfds;
    int	s;

    udt.it_interval.tv_sec = 0;
    udt.it_interval.tv_usec = timerDelay;
    udt.it_value.tv_sec = 0;
    udt.it_value.tv_usec = timerDelay;
    setitimer(ITIMER_REAL, &udt, 0);
    signal(SIGALRM, setflag);

    /* Idea:  read from client often, send to client not so often */
    while (1) {
	if (isClientDead()) {
	    if(!reconnect())
	       exit(0);
	}

        FD_ZERO(&readfds);
        FD_SET(sock, &readfds);
        if (udpSock >= 0)
            FD_SET(udpSock, &readfds);

	/* blocks indefinitely unless client input or timerDelay SIGALRM
	   causes EINTR */

        if(select(32, &readfds, 0, 0, NULL) > 0){
	    do_update = 0;	/* don't let updates interfere with read */

	    if (me->p_updates > delay) {
		  me->p_flags &= ~(PFWAR);
	    }
	    if (me->p_updates > rdelay) {
		  me->p_flags &= ~(PFREFITTING);
	    }

	    readFromClient();
	    do_update = 1;	/* ok now */
	    /* I think this can happen if a client read was interrupted */
	    if (sendflag) {
	       intrupt();
	       sendflag=0;
	    }
	}
    }
}

reconnect()
{
   int                  i;
   char                 buf[80];
   struct itimerval     udt;

   if (noressurect) exitGame();
   udt.it_interval.tv_sec = 0;
   udt.it_interval.tv_usec = 0;
   udt.it_value.tv_sec = 0;
   udt.it_value.tv_usec = 0;
   setitimer(ITIMER_REAL, &udt, 0);
   signal(SIGALRM, SIG_IGN);
   signal(SIGIO, SIG_IGN);
   printf("Ack!  The client went away!\n");
   printf("I will attempt to resurrect him!\n");
   fflush(stdout);
   commMode = COMM_TCP;
   if(udpSock >= 0)
       closeUdpConn();

   /* For next two minutes, we try to restore connection */
   shutdown(sock, 2);
   sock= -1;
   for (i=0;; i++) {
       me->p_ghostbuster = 0;
       sleep(5);
       if (connectToClient(host, nextSocket)) break;
       if (i==23) {
           printf("Oh well, maybe I'm getting rusty!\n");
           fflush(stdout);
           return 0;
       }
   }
   udt.it_interval.tv_sec = 0;
   udt.it_interval.tv_usec = timerDelay;
   udt.it_value.tv_sec = 0;
   udt.it_value.tv_usec = timerDelay;
   setitimer(ITIMER_REAL, &udt, 0);
   signal(SIGALRM, setflag);
   printf("A miracle!  He's alive!\n");
   fflush(stdout);
   return 1;
}