[comp.os.minix] Kermit performance

ast@cs.vu.nl (Andy Tanenbaum) (12/20/88)

After a certain about of effort, I now have MINIX-Kermit running on my AT
and talking to Kermit on a Sun over a 1200 baud line.  I seem to be able to
get an effective bandwidth of 1100 baud out of this combination.  This is not
bad at all considering that the Kermit packets are only 90 bytes, of which
5 are packet overhead.  Under these conditions the theoretical user bandwidth
is 1133 baud, so I am getting 97% efficiency.  I think it is fairly safe to
say that at 1200 baud, the MINIX RS232 driver is adequate for an AT.  It would
be interesting to hear statistics about over combinations of hardware/speeds.

Andy Tanenbaum (ast@cs.vu.nl)

brucee@runx.ips.oz (Bruce Evans) (12/27/88)

In <1835@ast.cs.vu.nl> ast@cs.vu.nl (Andy Tanenbaum) writes:
>is 1133 baud, so I am getting 97% efficiency.  I think it is fairly safe to
>say that at 1200 baud, the MINIX RS232 driver is adequate for an AT.  It would
>be interesting to hear statistics about over combinations of hardware/speeds.

I have rewritten the RS232 driver (3rd iteration this year), so that it works
at 9600 baud on a 5Mhz PC. The best throughput achieved was 901 chars/sec
using zmodem, sending from a 386 running DSZ.COM under DOS to the PC. Zmodem
under Minix from the 386 to the PC only achieves 580 chars/sec. Minix
kermits on each side reported 8630 baud on a 33K text file and 5560 baud
on a 102K binary. Kermit had compression on. This inflated the rate for
the text file.

The changes required for this are extensive. The basic problem in Minix
1.3 is that too many things turn off interrupts for long periods. On the
PC at 5MHz, you get only 200-300 instructions per millisec, so locking
more than this causes dropped characters at 9600 baud. My code mostly
avoids locks for more than 100 instructions, with a maximum of about 200.
Approximate numbers are:

                           save()   handler  interrupt()  restart()  latency

  RS232 interrupt          avoided     50      avoided     avoided      50
  RS232 output completion    34        50         50          17       151
  clock interrupt            34        80         50          17       181
  disk interrupt             34       none        50          17       101
  keyboard interrupt         34       116       avoided       17       167
  system call (switch)       26    not locked  not used       22        26

Note that these numbers rule out reliable operation at 19200 baud, but small
bursts should get through, consistent with what my system actually does.

In Minix 1.3, the corresponding numbers are about 70 for save() + restart(),
and (from distant memory) over 500 for handling a sendrec() system call, 200
to 300 for interrupt() in the best case, and several thousand for interrupt()
in the worst case (because of a loop). Any inadequacies in the RS232 driver
are just another straw.

Of course my faster system call handler and interrupt() improve system
performance generally, but not signifigantly except in the case of programs
doing single character i/o. But having a low level clock handler is worth 15%
on a PC.

Summary of changes:

rs232.c: Rewritten (dates from before the Minix rs232.c). It is almost
independendent of tty.c and has been used in a real-time application with
a throughput of 1700 chars/sec. The interrupt handlers are now in assembler.
The interface to tty.c consists of functions rs_read(), rs_write(), etc.,
which pass buffer pointers back and forth without requiring any copying
(even locally). The clock interrupt is used to wakeup TTY after RS232
activity.

tty.c: The interface to rs232.c is changed. Numerous bugs are fixed. Many
inner loops are rewritten. Calls to get_byte() are replaced by block
calls to phys_copy(), since get_byte() is not portable and was getting
in the way of my 386 port. Locks around loops are eliminated. Several
Kbytes of buffer space are reclaimed.

console.c: I tried not to get involved with it, but had to simplify the
keyboard interrupt handler (it was taking 2 millisec), and changed the
keyboard buffering method to the same as in rs232.c at the same time.
get_byte() also had to go, and some of the initialisation code was
relocated. Eventually, I want to split console.c into keyboard.c and
screen.c (coordinated by tty.c) and deny access to the tty structure
to both.

clock.c: rs_flush() is called at _every_ clock tick to let RS232 decide
whether to wake up TTY. This is best done independent of switching to the
clock task.

proc.c: Rewritten. Interrupt() was just too slow. Locking had to be done
locally and more carefully since there was no longer a global lock. These
changes are sufficient for fast RS232, but I also hid all the magic process
numbers using macros and reduced dynamic instruction counts wherever
possible.

mpx88.s: Rewritten. Small modifications allow sys_call() to run unlocked.
Everything was changed to reduce instruction counts.

*.h, *.c: magic process numbers are hidden.

I plan to post rs232.c, tty.c, console.c.cdif and a couple of other small
cdiffs early in January. If there is enough interest, I will see about
posting proc.c and its supporting changes.

Bruce Evans
Internet: brucee@runx.ips.oz.au    UUCP: uunet!runx.ips.oz.au!brucee