It's calling "write", which is a system call. Without the buffer, it would call write once for argv, and once for the newline if nflag is not set. Calling a system call twice would result in twice as many context switches, and thus be very slightly slower.
Speaking of clever tricks, would it be faster or slower to use c99's variable-length arrays instead of malloc?
But a single malloc is nothing compared to the cost of a context switch into kernel mode.
#define BUF_INITIAL 1024
char buf[BUF_INITIAL];
int main(int argc, char** argv)
{
char* p;
...
p = len > BUF_INITIAL ? malloc(len) : buf;
...
write(1, p, len);
...
}4 KB * (many) could be frightening.
I'm also guessing that 4k was chosen because a malloc() of 1 page is faster than a malloc() of >1 page. Of course that's with the assumption that the systems use a 4k page size.
"And why bother with stdio (or its Plan 9 equivalent) when it's so easy to do the buffering faster yourself?"
For the same reason you'd use any abstraction: to keep your program simpler. Even if you consider it "so easy" there are plenty of opportunities there for off-by-one errors and buffer overflows that would disappear if you used stdio instead.
My earliest UNIX programming was in the mid-80s on a machine with a (luxurious!) 1.5MB of RAM. I definitely remember avoiding stdio when writing small utilities that I wanted to start up and run fast.
Also remember that back in those days echo was not a shell builtin. (Hell, back then the testing operator '[' wasn't even a builtin. Some UNIXes like OS/X still have a vestigial /bin/[ executable!) Programs like echo that ran from shell scripts constantly had to be coded to start up as fast as possible.
This includes linux, but it's in /usr/bin/[
Put echo on a diet, removing unnecessary use of stdio and getopt.
Before...
-r-xr-xr-x 1 root wheel 58636 Oct 28 05:16 /bin/echo
After...
-rwxr-xr-x 1 root wheel 12824 Nov 12 17:39 /usr/obj/usr/src/bin/echo/echo
http://svnweb.freebsd.org/base?view=revision&revision=10... int main (int argc, const char * argv[])
{
for( int i = 1; i < argc; ++i)
{
((char *)argv[i])[-1] = ' ';
}
size_t len = strlen(argv[argc - 1]);
((char *)argv[argc - 1])[len] = '\n';
write(1, argv[1], argv[argc - 1] - argv[1] + len + 1);
return EXIT_SUCCESS;
}