A program to generate and save random numbers

In an earlier lecture I showed a program that generates a stream of pseudorandom integers and writes them out to the standard output.

Here is a slightly modified version of the program that instead writes the stream of integers out to a text file. The user supplies the name of the text file as a command line argument along with a seed for the random number sequence and the number of numbers to write.

#include <stdio.h>

int main (int argc, const char * argv[])
{
  int x, N, n;

  if(argc != 4)
  {
    printf("Invalid argument list.\n");
    printf("Usage: drnd <seed> <N> <filename>\n");
    return -1;
  }

  sscanf(argv[1],"%d",&x);
  sscanf(argv[2],"%d",&N);

  FILE* f = fopen(argv[3],"w");
  n = 0;
  while(n < N)
  {
    x = (17389*x+12553)%11657;
    n++;
    fprintf(f,"%d\n",x%10000);
  }
  fclose(f);

  return 0;
}

Writing the numbers in binary

The second version of today's sample program does the same thing, but this time the program writes the stream of integers to a binary file. By writing out the data in binary we skip the step where the integers have to be converted to text before saving them to the file. This program is roughly twice as fast as the text version.

#include <stdio.h>

int main (int argc, const char * argv[])
{
  int x, N, n, y;

  if(argc != 4)
  {
    printf("Invalid argument list.\n");
    printf("Usage: drnd <seed> <N> <filename>\n");
    return -1;
  }

  sscanf(argv[1],"%d",&x);
  sscanf(argv[2],"%d",&N);

  // Save the numbers in binary form
  FILE* f = fopen(argv[3],"wb");
  n = 0;
  while(n < N)
  {
    x = (17389*x+12553)%11657;
    n++;
    y = x%10000;
    fwrite(&y,sizeof(int),1,f);
  }
  fclose(f);

  return 0;
}

Using system calls

An alternative to using the fopen/fwrite/fclose combination is to use the equivalent system calls open/write/close. The third version of the program uses system calls instead of the standard library to write the data to the file in binary form.

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main (int argc, const char * argv[])
{
  int x, N, n, y;

  if(argc != 4)
  {
    printf("Invalid argument list.\n");
    printf("Usage: drnd <seed> <N> <filename>\n");
    return -1;
  }

  sscanf(argv[1],"%d",&x);
  sscanf(argv[2],"%d",&N);

  // Save the numbers in binary form
  int fd = open(argv[3],O_WRONLY|O_CREAT);
  n = 0;
  while(n < N)
  {
    x = (17389*x+12553)%11657;
    n++;
    y = x%10000;
    write(fd,&y,sizeof(int));
  }
  close(fd);

  return 0;
}

The version turns out to be incredibly slow. The reason for the poor performance is that we are doing N separate calls to write() to write a stream of N integers to the file. Unlike fwrite(), the write() system call uses unbuffered writes. When you make a call to fwrite(), the data you ask to write first gets accumulated in a buffer in memory. When the buffer gets full or you close the file, the contents of the buffer get dumped to the file all at once. When you make a call to write(), the data you requested gets written to the file immediately. Making a large number of calls to write() where you write a small amount of data each time is very inefficient.

System calls with a buffer

The more efficient way to use write() is to put all of your data in a buffer and then write the entire buffer to the file at once. The fourth and final version of our example program does just that.

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

int main (int argc, const char * argv[])
{
  int x, N, n;

  if(argc != 4)
  {
    printf("Invalid argument list.\n");
    printf("Usage: drnd <seed> <N> <filename>\n");
    return -1;
  }

  sscanf(argv[1],"%d",&x);
  sscanf(argv[2],"%d",&N);

  int* A = (int*) malloc(N*sizeof(int));

  // Generate the numbers
  n = 0;
  while(n < N)
  {
    x = (17389*x+12553)%11657;
    A[n] = x%10000;
    n++;
  }

  // Dump all the numbers to the file at once
  int fd = open(argv[3],O_WRONLY|O_CREAT);
  write(fd,A,N*sizeof(int));
  close(fd);

  free(A);

  return 0;
}

Further reading

Our course textbook has very good coverage of both the system calls and the standard library functions for files. You will find a discussion of the system calls in chapter two. I recommend reading chapter two up to but not including the section on Synchronized I/O. You will find an in-depth discussion of the standard library file functions in chapter three.