epsilon@wet.UUCP (Eric P. Scott) (07/16/89)
Many, many screen-oriented applications posted to the net have a serious flaw: they do terminal output a character at a time. This is fine for single-user PCs running MSDOS. This is NOT fine for multi-user machines running UNIX. The culprit here is Standard I/O; stdout is not buffered when directed to a terminal. (System V has line buffering, but it's not the appropriate solution here.) This means that a write(2) call is performed for each character output. This has substantial overhead on many machines--not only does your program run slowly, response time for everyone else is degraded. The fix is very simple: put setbuf(stdout, malloc(BUFSIZ)); early in main(), and (if you've done any output) fflush(stdout); before asking for terminal input, sleeping, going heavily compute-bound, changing tty modes, or using fork/exec/system-type calls. This causes stdio to store up to BUFSIZ (defined in <stdio.h>) characters before demanding a context switch. Since BUFSIZ is typically 512 or 1024, this can be a substantial improvement. Make sure you have declared malloc to return the proper type for your system (either char * or void *). I urge source-group moderators to perform a cursory check before redistributing submissions. Note that the absence of setbuf/ setvbuf/setlinebuf calls does not automatically damn a program, it just means that closer inspection is needed. This issue comes up often enough that it might warrant a mention in regular monthly/quarterly postings. -=EPS=- "We haven't found a cure for people who insist on writing printf("\n"); but our efforts at rehabilitation have been largely successful."
nelson@sun.soe.clarkson.edu (Russ Nelson) (07/16/89)
In article <283@wet.UUCP> epsilon@wet.UUCP (Eric P. Scott) writes:
Many, many screen-oriented applications posted to the net have a
serious flaw: they do terminal output a character at a time.
This is fine for single-user PCs running MSDOS. ...
No it isn't. "Kernel"[1] calls in MS-LOSS have quite a big of overhead,
and the difference between buffered and unbuffered I/O *is* noticable.
So please, use setbuf in your MS-LOSS programs also.
[1] To the extent that MS-LOSS can be said to have a kernel.
--
--russ (nelson@clutx [.bitnet | .clarkson.edu])
I'm not the NRA, and I vote twice.
guy@auspex.auspex.com (Guy Harris) (07/18/89)
>The fix is very simple: put > setbuf(stdout, malloc(BUFSIZ)); > >early in main(), In which case, if you run out of e.g. swap space (or address space, unless you've got tons of it and haven't consumed it all due to some other bug), you may drop core. The fix is very simple; either do register char *bufp; ... if ((bufp = malloc(BUFSIZ)) == NULL) { complain and exit; } setbuf(stdout, bufp); or just do: static char stdoutbuf[BUFSIZ]; ... setbuf(stdout, stdoutbuf); in "main()". (I say "static" because if you return from "main()", it's like exiting, and standard output will be flushed; better safe than sorry.) "malloc" is overkill in this situation....
chad@lakesys.UUCP (D. Chadwick Gibbons) (07/18/89)
In article <2245@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes: | |>The fix is very simple: put |> setbuf(stdout, malloc(BUFSIZ)); | |In which case, if you run out of e.g. swap space (or address space, |unless you've got tons of it and haven't consumed it all due to some |other bug), you may drop core. Uh, no. Sorry. Read the man page for setbuf a little better. malloc() returns NULL when it can't allocate the memory, right? If the second argument to setbuf is NULL, buffering will be turned off. No dumping core, just a straight through output mechanism-which might be nice to have considering your program can't allocate memory and it is early on in the game. -- D. Chadwick Gibbons, chad@lakesys.lakesys.com, ...!uunet!marque!lakesys!chad
epsilon@wet.UUCP (Eric P. Scott) (07/19/89)
In article <2245@auspex.auspex.com> guy@auspex.auspex.com (Guy Harris) writes: >>The fix is very simple: put >> setbuf(stdout, malloc(BUFSIZ)); >> >>early in main(), > >In which case, if you run out of e.g. swap space (or address space, >unless you've got tons of it and haven't consumed it all due to some >other bug), you may drop core. I got a mail message claiming the same thing--on which system does it occur? All the ones I know of simply have malloc() return a NULL pointer, which is valid input to setbu()--it sets the stream unbuffered. >[suggested checking malloc's return value] In any case, if the first thing you do in a program is malloc() and it fails, you're probably in serious trouble anyway. (I normally practice a fairly paranoid programming style, this being one of the exceptions.) Also, NULL should always be cast to a pointer of the desired type when used explicitly. I prefer if (!(x)) to test for this case; the ! operator is true against a NULL pointer type, \even if/ your architecture uses something other than 0 to represent NULL, and false otherwise. >[suggested using a static buffer] I've run across stdio implementations that are documented to only work with buffers obtained from malloc(). (I presume this means they free() on fclose, or somesuch nonsense. There should be a "MYBUF" flag, but since that isn't part of the documented interface, some vendors didn't include it. SunOS users: your stdio is different from all UNIX flavors I know about. See the setbuf(3s) manual page. BSD users will probably want to use the BSD-specific buffering functions as well. It's a lot easier to change a setbuf() to something "better" than to find all the places to fflush if it's not done automatically as a side effect of reading stdin. I.e. plan for fully buffered terminal output. -=EPS=-
prc@erbe.se (Robert Claeson) (07/19/89)
In article <283@wet.UUCP> epsilon@wet.UUCP (Eric P. Scott) writes: >The fix is very simple: >put > setbuf(stdout, malloc(BUFSIZ)); >early in main(), and (if you've done any output) > fflush(stdout); >before asking for terminal input, sleeping, going heavily >compute-bound, changing tty modes, or using fork/exec/system-type >calls. This causes stdio to store up to BUFSIZ (defined in ><stdio.h>) characters before demanding a context switch. Since >BUFSIZ is typically 512 or 1024, this can be a substantial >improvement. Or, on at least System V-type systems, use the call: #define BUFFER_SIZE 32768 setvbuf(stdout, malloc(BUFFER_SIZE), _IOFBF, BUFFER_SIZE); which can buffer much more than the setbuf() construct above. The buffer can be of arbitrary size. i n e w s -- Robert Claeson E-mail: rclaeson@erbe.se ERBE DATA AB
rjc@edai.ed.ac.uk (Richard Caley) (07/20/89)
In article <857@lakesys.UUCP> chad@lakesys.UUCP (D. Chadwick Gibbons) writes: >|>The fix is very simple: put >|> setbuf(stdout, malloc(BUFSIZ)); But why use malloc at all, seems odd to dynamically allocats something which you will need every run and which will always be the same size . . . static char buffer[BUFSIZ]; setbuf(stdout,&buffer);