Jump to: navigation, search

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);
}
Personal tools