The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 8.01 on page 174
Rewrite the program cat
from Chapter 7 using read
, write
, open
and close
instead of their standard library equivalents. Perform experiments to determine the relative speeds of the two versions.
Solution by Andrew Tesker
/* Andrew Tesker ucat.c a version of cat using UNIX system access */ #include <stdio.h> #include <fcntl.h> #define BUFSIZE 1024 int main(int argc, char *argv[]) { int fd1; void filecopy(int f, int t); if(argc == 1) filecopy(0, 1); else { while(--argc > 0) if(( fd1 = open(*++argv, O_RDONLY, 0)) == -1) { printf("unix cat: can't open %s\n", *argv); return 1; } else { filecopy(fd1, 1); close(fd1); } } return 0; } void filecopy(int from, int to) { int n; char buf[BUFSIZE]; while((n=read(from, buf, BUFSIZE)) > 0 ) write(to, buf, n); }
Solution by codybartfast
Using the standard library took 34% longer than using system calls.
| Times sec (256MB) | Avrg |%linux |%syscal --------|-----------------------|-------|-------|------- lnxcat: | 9.590, 9.502, 9.721 | 9.604 | |-22.7% syscal: |12.378, 12.465, 12.421 |12.421 |+29.3% | stdlib: |16.462, 16.524, 16.980 |16.655 |+73.4% |+34.1%
#include <fcntl.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> static void filecopy(int ifd, char *ipath, int ofd, char *opath); static void error(char *fmt, ...); int main(int argc, char *argv[]) { int fd; char *path; if (argc == 1) { fd = 0; path = "<stdin>"; } else if ((fd = open(path = argv[1], O_RDONLY, 0)) == -1) { error("%s: can't open %s\n", argv[0], path); } filecopy(fd, path, 1, "<stdout>"); if (fd) close(fd); return 0; } void filecopy(int ifd, char *ipath, int ofd, char *opath) { static char buf[BUFSIZ]; int n; while ((n = read(ifd, buf, BUFSIZ)) > 0) if (write(ofd, buf, n) != n) error("Problem writing to '%s'", opath); if (n == -1) error("Problem reading from '%s'", ipath); } void error(char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "error: "); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); exit(1); }
Latest code on github
Solution by anonymous
I used the code from pages 162 and 163 and kept the logic mostly the same. I added the error function recently introduced and updated the filecopy function to work with the system call version. I noticed that the book forgot to include the "syscalls.h" file, but with a bit of searching around on the Internet, I discovered that I could use #include <unistd.h> in its place.
For my testing, I noticed that the system call version's speed greatly depends on the BUFSIZ. When I changed BUFSIZ from 8192 to 1 so both versions read 1 character at a time, it was much slower (99.23% slower on average). However, when I left the BUFSIZ alone, the speed was insanely faster (85% faster on average). This was true for large files as well. I did the test on a 10 GB file and the system call version took 18 seconds while the standard C version took 193 seconds (91% slower). So it seems that as long as the buffer is a decent size for the tool reading in data from the file system, it will be much faster than something reading in one byte at a time.
Here is my code
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <stdarg.h> /* Exercise 8-1. Rewrite the program cat from Chapter 7 using read, write, open and close instead of their standard library equivalents. Perform experiments to determine the relative speeds of the two versions. */ enum fileDescriptors { STDIN, STDOUT, STDERR }; void filecopy(int src, int dst); void error(char *fmt, ...); int main(int argc, char *argv[]) { int f; if (argc == 1) // no args; copy standard input to output filecopy(STDIN, STDOUT); else while (--argc > 0) if ((f = open(*++argv, O_RDONLY, 0)) == -1) error("can't open %s", *--argv); else { filecopy(f, STDOUT); close(f); // close the file since finished reading it } exit(0); } // copy src to dst void filecopy(int src, int dst) { int n; char buf[BUFSIZ]; while ((n = read(src, buf, BUFSIZ)) > 0) if (write(dst, buf, n) != n) error("unable to copy src to dst"); } // print an error message and die void error(char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "error: "); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); va_end(args); exit(1); }