Slow console driver
egray at fthood.UUCP
egray at fthood.UUCP
Sun Oct 2 03:46:00 AEST 1988
Howdy...
This is a weird story, so bear with me...
Introduction:
I've noticed that Pcomm doesn't appear to display characters on
the screen as fast as I think it ought to. For example, if
you've got a 2400 baud modem attached to tty000, the screen
still appears to display things at a rate closer to 1200 baud.
This only seems to affect the screen display, not the file
transfers.
In an effort to make the Pcomm screens faster, I've been
performing some simple tests to determine the best method of
doing unbuffered character writes.
What I've found:
1) There is a *huge* difference in speed between sending a
character to the console at 9600 and sending it to a terminal on
/dev/tty000 at 9600 baud. For example, the average speed on the
console is in the range of 120 to 135 cps (characters per
second). However, the average speed using tty000 is in the
range 620 to 700 cps.
Obviously, using buffered writes to the screen you can obtain
numbers much greater than this... but that's not an alternative
in a telecommunications program.
2) Methods using the normal tty interface (the "cooked mode")
did better than the "raw" mode. This is strange since the
raw mode doesn't have to do output postprocessing. I had bet
that the raw mode would have been faster....
3) I had assumed that since "putchar(c)" is a macro to
"putc(c, stdout)" that the times would have been identical.
They are *very* close, but still different...
4) The "control" used in the test was a method using full
buffered fwrite()s. On the average, the console obtained about
32% of its control value, and the tty000 obtained about 70% of
its control value.
Conclusions:
The tty driver for the console is a strange beast. It doesn't
like any method of unbuffered character writes (it doesn't do
all that hot using full buffered writes either).
The details of the test:
Unix PC with 2 Meg RAM and Unix 3.51 (console at 9600 baud)
Toshiba T1200 on tty000 running ProComm Plus at 9600 baud.
The test program (that is contained below) uses the following
methods of writing to the screen:
1) cooked, setbuf, fputc:
Uses the normal tty interface (the cooked mode), uses
a "setbuf(stdout, (char *) NULL)" at the beginning to
make the output unbuffered, uses "fputc(c, stdout)"
to output the characters.
2) cooked, setbuf, putc:
Identical to above except it uses "putc(c, stdout)"
instead of "fputc()".
3) cooked, setbuf, putchar:
Same as above, but uses "putchar()".
4) cooked, fputc, fflush:
Uses normal tty buffering (without the "setbuf()"),
but follows every "fputc(c, stdout)" with a
"fflush(stdout)" to make it unbuffered.
5) cooked, putc, fflush:
Same as above but uses "putc()" instead of "fputc()"
6) cooked, putchar, fflush:
Same as above but uses "putchar()".
7) cooked, write:
Uses just "write(1, &c, 1)".
8) cooked fwrite:
Uses full block buffered fwrite()s. Not really a test
method... used as a control.
9-16) Same as above but done in the "cbreak" (raw) mode
with no post processing.
Since the test is to measure what is actually sent to the screen,
the "cooked" mode test results take into account that the tty
driver has added a CR to every NL.
All tests were done on a test file that was 3994 characters
long (the source to the test program). The test was performed as:
testit testfile 2> results
You'll notice that the CPS in one case exceeds the 960 cps
theoretical limit (9600 baud / 10 bits per byte = 960 cps).
So, don't go to the bank on any of these numbers... they are
only for comparing the different write methods.
The test results on the console (at 9600 baud):
Method real user sys CPS
cooked, setbuf, fputc 30.52 0.50 4.70 137.4
cooked, setbuf, putc 30.78 0.78 4.85 136.2
cooked, setbuf, putchar 30.55 0.62 4.82 137.3
cooked, fputc, fflush 31.23 1.28 5.00 134.3
cooked, putc, fflush 31.17 1.08 5.40 134.6
cooked, putchar, fflush 31.05 0.98 4.93 135.1
cooked, write 30.42 0.47 5.42 137.9
cooked, fwrite 9.28 0.00 0.42 451.8
raw, setbuf, fputc 33.00 0.63 4.92 121.0
raw, setbuf, putc 33.07 0.72 4.87 120.8
raw, setbuf, putchar 32.97 0.68 4.40 121.2
raw, fputc, fflush 33.63 1.48 4.58 118.8
raw, putc, fflush 33.43 1.27 4.52 119.5
raw, putchar, fflush 33.57 1.45 3.93 119.0
raw, write 33.17 0.53 4.95 120.4
raw, fwrite 11.35 0.00 0.08 351.9
cooked file length = 4194
raw file length = 3994
The test results on tty000 at 9600 baud:
Method real user sys CPS
cooked, setbuf, fputc 5.92 0.30 5.62 708.8
cooked, setbuf, putc 5.83 0.20 5.63 719.0
cooked, setbuf, putchar 5.82 0.12 5.70 721.0
cooked, fputc, fflush 6.57 0.72 5.68 638.7
cooked, putc, fflush 6.28 0.47 5.82 667.5
cooked, putchar, fflush 6.28 0.43 5.85 667.5
cooked, write 5.63 0.08 5.55 744.5
cooked, fwrite 4.40 0.00 0.47 953.2
raw, setbuf, fputc 6.00 0.45 5.55 665.7
raw, setbuf, putc 5.92 0.23 5.68 675.0
raw, setbuf, putchar 5.92 0.32 5.60 675.0
raw, fputc, fflush 6.45 0.72 5.73 619.2
raw, putc, fflush 6.37 0.47 5.90 627.3
raw, putchar, fflush 6.48 0.57 5.82 616.0
raw, write 5.75 0.02 5.73 694.6
raw, fwrite 4.02 0.00 0.07 994.4
cooked file length = 4194
raw file length = 3994
Emmet P. Gray US Army, HQ III Corps & Fort Hood
...!uunet!uiucuxc!fthood!egray Attn: AFZF-DE-ENV
Directorate of Engineering & Housing
Environmental Management Office
Fort Hood, TX 76544-5057
-------------------------------------------------------------------------------
/*
* Test the speed of the screen write methods.
*/
#include <stdio.h>
#include <termio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/times.h>
#define TICK 60.0
int num_lines, num_chars;
main(argc, argv)
int argc;
char *argv[];
{
FILE *fp;
int i, j, n, num, len_cooked, len_raw;
long start, stop;
float real, user, system, cps;
char buf[1024];
struct termio tbuf, thold;
struct tms startbuf, stopbuf;
if (argc != 2) {
printf("requires a text file argument to be used as a test\n");
exit(1);
}
if (!(fp = fopen(argv[1], "r"))) {
perror("fopen");
exit(1);
}
/* get raw length */
get_nums(argv[1]);
len_cooked = num_chars + num_lines;
len_raw = num_chars;
ioctl(0, TCGETA, &tbuf);
ioctl(0, TCGETA, &thold);
fprintf(stderr, " Method real user sys CPS\n\n");
for (j=0; j<16; j++) {
switch(j) {
case 0:
fprintf(stderr, "cooked, setbuf, fputc ");
break;
case 1:
fprintf(stderr, "cooked, setbuf, putc ");
break;
case 2:
fprintf(stderr, "cooked, setbuf, putchar ");
break;
case 3:
fprintf(stderr, "cooked, fputc, fflush ");
break;
case 4:
fprintf(stderr, "cooked, putc, fflush ");
break;
case 5:
fprintf(stderr, "cooked, putchar, fflush ");
break;
case 6:
fprintf(stderr, "cooked, write ");
break;
case 7:
fprintf(stderr, "cooked, fwrite ");
break;
case 8:
fprintf(stderr, "raw, setbuf, fputc ");
break;
case 9:
fprintf(stderr, "raw, setbuf, putc ");
break;
case 10:
fprintf(stderr, "raw, setbuf, putchar ");
break;
case 11:
fprintf(stderr, "raw, fputc, fflush ");
break;
case 12:
fprintf(stderr, "raw, putc, fflush ");
break;
case 13:
fprintf(stderr, "raw, putchar, fflush ");
break;
case 14:
fprintf(stderr, "raw, write ");
break;
case 15:
fprintf(stderr, "raw, fwrite ");
break;
}
n = j;
if (n >= 8) {
n = n -8;
tbuf.c_cc[4] = 1;
tbuf.c_cc[5] = 0;
tbuf.c_iflag = 0;
tbuf.c_oflag = 0;
tbuf.c_lflag = 0;
tbuf.c_cflag &= ~PARENB;
tbuf.c_cflag &= ~CSIZE;
tbuf.c_cflag |= CS8;
}
ioctl(0, TCSETAW, &tbuf);
freopen("/dev/tty", "w", stdout);
if (n <= 2)
setbuf(stdout, (char *) NULL);
/* start the timer */
start = times(&startbuf);
while ((num = fread(buf, sizeof(char), BUFSIZ, fp)) > 0) {
switch(n) {
case 0:
for (i=0; i<num; i++)
fputc(buf[i], stdout);
break;
case 1:
for (i=0; i<num; i++)
putc(buf[i], stdout);
break;
case 2:
for (i=0; i<num; i++)
putchar(buf[i]);
break;
case 3:
for (i=0; i<num; i++) {
fputc(buf[i], stdout);
fflush(stdout);
}
break;
case 4:
for (i=0; i<num; i++) {
putc(buf[i], stdout);
fflush(stdout);
}
break;
case 5:
for (i=0; i<num; i++) {
putchar(buf[i]);
fflush(stdout);
}
break;
case 6:
for (i=0; i<num; i++)
write(1, &buf[i], 1);
break;
case 7:
fwrite(buf, sizeof(char), num, stdout);
break;
}
}
/* stop the timer */
stop = times(&stopbuf);
real = (float) (stop - start) / TICK;
user = (float) (stopbuf.tms_utime - startbuf.tms_utime) / TICK;
system = (float) (stopbuf.tms_stime - startbuf.tms_stime) / TICK;
if (j >= 8)
cps = len_raw / real;
else
cps = len_cooked / real;
fprintf(stderr, "\t%10.2f%10.2f%10.2f%11.1f\n", real, user, system, cps);
rewind(fp);
ioctl(0, TCSETAW, &thold);
}
fprintf(stderr, "\ncooked file length = %d\n", len_cooked);
fprintf(stderr, "raw file length = %d\n", len_raw);
printf("\r\n");
}
get_nums(file)
char *file;
{
FILE *fp;
char buf[BUFSIZ];
struct stat sbuf;
num_chars = 0;
num_lines = 0;
if (stat(file, &sbuf) < 0)
return;
num_chars = sbuf.st_size;
if (!(fp = fopen(file, "r")))
return;
while (fgets(buf, BUFSIZ, fp) != NULL)
num_lines++;
fclose(fp);
return;
}
More information about the Unix-pc.general
mailing list