Subject: IPC Test program
To: None <current-users@sun-lamp.cs.berkeley.edu>
From: Mark P. Gooderum <mark@aggregate.com>
List: current-users
Date: 05/06/1994 14:23:23
After 10 requests already for this I figured it was worth posting.
If someone wants to implement some of the unimplmented SysV IPC 
testing, go to it.

There is a -z debug flag as well.  Building on SysVen will usually
require -lsocket (and maybe -lnsl).  The formatting is kind of futzed
because I use 4 char tab stops.  Use indent in emacs to fix it.  If
you use vi, that's you're problem ;-).

Change the value of BASE_MSG to change the total byte volume to get
long enough runs to have good numbers and short enough runs to 
go home.  These numbers are good for 486 up to slow Suns.  Faster
boxes and screamers require much larger numbers to run long enough to get
good timings.

-Mark
------
/*
 * ipc.c - A program to measure local IPC speed.
 */

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>

#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/*
 * Constants...
 */

#define BASE_MSG	(1048576 / 16)
#undef TRUE
#undef FALSE
#define TRUE 1
#define FALSE 0

/*
 * Type Declarations...
 */
typedef struct ipc_stat {
	int		io_size;		/* # of bytes per I/O op					*/
	int		io_total;		/* Total # of bytes done					*/
	int		millisec;		/* # millsecsonds to do types for all I/O	*/
	double	io_kps;			/* K bytes per/second I/O speed				*/
	double  msg_ps;			/* # messages per second					*/
} ipc_stat; 

/*
 * Variables...
 */

int	size_list[] = { 16, 256, 1024, 4096, -1 };

ipc_stat	stat_tab[100];

char		write_buf[4096], 
			read_buf[4096];

int		sock_domain_stream = FALSE;
int		sock_domain_dgram = FALSE;
int		sock_tcp = FALSE;
int		sock_udp = FALSE;
int		msg_port = FALSE;
int		stream_pipe = FALSE;
int		debug_flag = FALSE;

/*
 * Socket stuff...
 */

struct sockaddr_in	maddr, saddr;
struct sockaddr aaddr;

/* 
 * Support functions...
 */
void usage(void);
void print_stats(char *, ipc_stat *);
ipc_stat *do_io(int *, ipc_stat *);
int sock_master(int);
int sock_stream(int);

/*
 * Measurement functions...
 */

ipc_stat *do_sock_domain_stream();
ipc_stat *do_sock_domain_dgram();
ipc_stat *do_sock_udp();
ipc_stat *do_sock_tcp();
ipc_stat *do_stream_pipe();

/*
 * Support functions...
 */

void main(int argc, char *argv[])
{
	ipc_stat	*stats;
	char		opt;

	/*
	 * Check for args...
	 */

	if (argc <= 1) {
		usage();
		exit(0);
	}

	/*
	 * Parse our argument list...
	 */

	while ((opt = getopt(argc, argv, "adDptumz")) != EOF) {
		switch(opt) {
			case 'a':
			sock_domain_stream = TRUE;
			sock_domain_dgram = TRUE;
			sock_tcp = TRUE;
			sock_udp = TRUE;
			stream_pipe = TRUE;
			msg_port = TRUE;
			break;

			case 'd':
			sock_domain_stream = TRUE;
			break;

			case 'D':
			sock_domain_dgram = TRUE;
			break;

			case 't':
			sock_tcp = TRUE;
			break;

			case 'u':
			sock_udp = TRUE;
			break;

			case 'm':
			msg_port = TRUE;
			break;

			case 'p':
			stream_pipe = TRUE;
			break;

			case 'z':
			debug_flag = TRUE;
			break;

			default:
			usage();
			exit(1);

		} /* switch */
	} /* while */

	/*
	 * Do our measurements and print the results for each
	 * IPC method.
	 */

	if (sock_domain_stream) {
		stats = do_sock_domain_stream();
		print_stats("Stream Domain Sockets", stats);
	}
	
	if (sock_domain_dgram) {
		stats = do_sock_domain_dgram();
		print_stats("Datagram Domain Sockets", stats);
	}
	
	if (sock_udp) {
		stats = do_sock_udp();
		print_stats("UDP Sockets", stats);
	}
	
	if (sock_tcp) {
		stats = do_sock_tcp();
		print_stats("TCP Sockets", stats);
	}
	
	if (msg_port) {
		printf("Warning, '-m' - SysV Messages, currently not supported.\n");
	}
	
	if (stream_pipe) {
		stats = do_stream_pipe();
		print_stats("Stream Pipe", stats);
	}
} /* main() */

/*
 * usage()
 */
void usage(void)
{
	printf("\nUsage: ipc [-dtums]\n");
	printf("\t-a\t-Time all.\n");
	printf("\t-d\t-Time Stream Domain Sockets\n");
	printf("\t-D\t-Time Datagram Domain Sockets\n");
	printf("\t-t\t-Time TCP Sockets\n");
	printf("\t-u\t-Time UDP Sockets\n");
	printf("\t-m\t-Time Message Ports\n");
	printf("\t-p\t-Time Stream Pipes\n");
	printf("\n");
}


/*
 * do_sock_domain_stream()
 */
ipc_stat *do_sock_domain_stream(void)
{
	ipc_stat	*retval;
	int			fds[2];
	int			x;

	/*
	 * Initialize some variables.
	 */

	retval = stat_tab;

	/*
	 * Loop on I/O sizes calling do_io()...
	 */

	for (x = 0; size_list[x] != -1; x++) {
		retval[x].io_size = size_list[x];
		if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
			perror("socketpair(SOCK_STREAM) failed");
			retval[0].io_size = -1;
			return(NULL);
		}
		do_io(fds, &retval[x]);
		close(fds[0]);
		close(fds[1]);
	}
	retval[x].io_size = -1;
	
	/*
	 * And return...
	 */

	return(retval);

} /* do_sock_domain_stream() */


/*
 * do_sock_domain_dgram()
 */
ipc_stat *do_sock_domain_dgram(void)
{
	ipc_stat	*retval;
	int			fds[2];
	int			x;

	/*
	 * Initialize some variables.
	 */

	retval = stat_tab;

	/*
	 * Loop on I/O sizes calling do_io()...
	 */

	for (x = 0; size_list[x] != -1; x++) {
		retval[x].io_size = size_list[x];
		if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) == -1) {
			perror("socketpair(SOCK_DGRAM) failed");
			retval[0].io_size = -1;
			return(NULL);
		}
		do_io(fds, &retval[x]);
		close(fds[0]);
		close(fds[1]);
	}
	retval[x].io_size = -1;

	/*
	 * And return...
	 */

	return(retval);

} /* do_sock_domain_dgram() */


/*
 * do_sock_tcp()
 */
ipc_stat *do_sock_tcp(void)
{
	ipc_stat	*retval;
	int			fds[2];
	int			x, ts, ssize;

	/*
	 * Initialize some variables.
	 */

	ssize = sizeof(aaddr);
	retval = stat_tab;

	/*
	 * Loop on I/O sizes calling do_io()...
	 */

	for (x = 0; size_list[x] != -1; x++) {
		retval[x].io_size = size_list[x];

		/*
		 * Initialize our fd's...
		 */
		
		if ((ts = sock_master(SOCK_STREAM)) < 0) {
			return(NULL);
		}
		if ((fds[1] = sock_slave(SOCK_STREAM)) < 0) {
			return(NULL);
		}
		if ((fds[0] = accept(ts, &aaddr, &ssize)) < 0) {
			return(NULL);
		} 

		do_io(fds, &retval[x]);
		close(fds[0]);
		close(fds[1]);
		close(ts);
	}
	retval[x].io_size = -1;

	/*
	 * And return...
	 */

	return(retval);

} /* do_sock_tcp() */


/*
 * do_sock_udp()
 */
ipc_stat *do_sock_udp(void)
{
	ipc_stat	*retval;
	int			fds[2];
	int			x, ts, ssize;

	/*
	 * Initialize some variables.
	 */

	ssize = sizeof(aaddr);
	retval = stat_tab;

	/*
	 * Initialize our fd's...
	 */

	/*
	 * Loop on I/O sizes calling do_io()...
	 */

	for (x = 0; size_list[x] != -1; x++) {
		retval[x].io_size = size_list[x];
		/*
		 * Initialize our fd's...
		 */
		
		if ((fds[1] = sock_master(SOCK_DGRAM)) < 0) {
			return(NULL);
		}
		if ((fds[0] = sock_slave(SOCK_DGRAM)) < 0) {
			return(NULL);
		}
		do_io(fds, &retval[x]);
		close(fds[0]);
		close(fds[1]);
		close(ts);
	}
	retval[x].io_size = -1;

	/*
	 * And return...
	 */

	return(retval);

} /* do_sock_udp() */


/*
 * do_stream_pipe()
 */
ipc_stat *do_stream_pipe(void)
{
	ipc_stat	*retval;
	int			fds[2];
	int			x;

	/*
	 * Initialize some variables.
	 */

	retval = stat_tab;

	/*
	 * Loop on I/O sizes calling do_io()...
	 */

	for (x = 0; size_list[x] != -1; x++) {
		retval[x].io_size = size_list[x];
		if (pipe(fds) == -1) {
			perror("socketpair(SOCK_STREAM) failed");
			retval[0].io_size = -1;
			return(NULL);
		}
		do_io(fds, &retval[x]);
		close(fds[0]);
		close(fds[1]);
	}
	retval[x].io_size = -1;

	/*
	 * And return...
	 */

	return(retval);

} /* do_stream_pipe() */


/*
 * do_io()
 *
 * Do the I/O on a given descriptor (writing) and measure it.
 */
ipc_stat *do_io(int *fd, ipc_stat *istat)
{
	struct timeval	begtime, endtime;
	register int	x;
	register int	num_msgs;
	int				pid, ret;
	int				size;
	int				tot_size;
	register int	fd1 = fd[0],
					fd2 = fd[1];

	size =  istat->io_size;
	num_msgs = BASE_MSG / (int) log2(size);
	tot_size = num_msgs * size;

	if (debug_flag) {
		printf("do_io(): Size=%d, msgs=%d\n", size, num_msgs);
	}
	if ((pid = fork()) == -1) {
		return(NULL);
	}

	gettimeofday(&begtime);
	if (pid > 0) {
		close(fd2);
		for (x = 0; x < num_msgs ; x++) {
			writen(fd1, write_buf, size);
		}
	}
	if (pid == 0) {
		close(fd1);
		for (x = 0; x < num_msgs ; x++) {
			readn(fd2, read_buf, size);
		}
		_exit(0);
	}
	kill(pid, SIGTERM);
	do {
		ret = wait(NULL);
	} while (ret != pid);
	gettimeofday(&endtime);
	istat->io_total = tot_size;
	istat->millisec = (((endtime.tv_sec * 1000) + (endtime.tv_usec / 1000)) -
					   ((begtime.tv_sec * 1000) + (begtime.tv_usec / 1000)));
	istat->io_kps = ((double) istat->io_total) /
					((double) istat->millisec) * 1000.0
		             / 1024.0;
	istat->msg_ps = ((double) num_msgs * 1000.0) / ((double)(istat->millisec));
	return(istat);
} /* do_io() */


/*
 * print_stats()
 */
void print_stats(char *io_type, ipc_stat *stats)
{
	int		x;

	printf("Timing for %s:\n", io_type);
	printf("--------------------------------\n");
	for (x = 0; stats[x].io_size != -1; x++) {
		printf("Size: %d,\tKB p/sec: %.2f,\tMsgs p/sec: %.2f\n",
			   stats[x].io_size, stats[x].io_kps, stats[x].msg_ps);
	}
	printf("\n");
} /* print_stats() */

/*
 * readn()
 *
 * Read n bytes from a stream socket.  A stream socket may return 0 
 * read but still have I/O because of buffering constraints.
 */
int readn(fd, parg, nbytes)
  int		fd;
  void		*parg;
  int		nbytes;
{
  char		*ptr= parg;
  int		nleft, 
  		nread,
  		ntot = 0;

  /*
   * Loop until we read() nbytes bytes or until we get an error (which
   * may be EWOULDBLOCK if it's a non-blocking socket.
   */

  nleft = nbytes;
  while (nleft > 0) {
    nread = read(fd, ptr, nleft);
    if (nread < 0) {

      /*
       * If we have read some bytes throw away the error and return what
       * we have read, otherwise return the error.
       */

      return(ntot > 0 ? ntot : nread);
    } else if (nread == 0) {
      break;
    }
    ntot += nread;
    nleft -= nread;
    ptr += nread;
  }
  return(nbytes - nleft);
} /* readn() */


/*
 * writen()
 *
 * Write n bytes to a stream socket.
 */
int writen(fd, parg, nbytes)
  int		fd;
  void		*parg;
  int		nbytes;
{
  char		*ptr = parg;
  int		nleft = nbytes,
  		ntally = 0,
  		nwritten;

  while (nleft > 0) {
    nwritten = write(fd, ptr, nleft);
    if (nwritten < 0) {
      return(ntally > 0 ? ntally : nwritten);
    } else if (nwritten == 0) {
      break;
    }
    nleft -= nwritten;
    ntally += nwritten;
    ptr += nwritten;
  }
  return(nbytes - nleft);
} /* writen() */


/*
 * sock_master()
 * 
 * This function opens the master half of a Unix socket.
 * We use a sock of the name <jobid.iosock>
 */
static int sock_master(type)
  int		type;
{
  int		master_fd;
  int		alen;
  int		retval;
  struct hostent *host;

  if ((master_fd = socket(AF_INET, type, 0)) < 0) {
    return(-1);
  }

  memset(&maddr, 0, sizeof(maddr));
  maddr.sin_family = AF_INET;
  maddr.sin_addr.s_addr = htonl(INADDR_ANY);
  maddr.sin_port = 0;
  alen = sizeof(maddr);
  if (bind(master_fd, &maddr, alen) < 0) {
    close(master_fd);
    return(-1);
  }
  if (getsockname(master_fd, &saddr, &alen) < 0) {
    close(master_fd);
    return(-1);
  }
  if ((host = gethostbyname("localhost")) == NULL) {
	  return(-1);
  }
  memcpy(&saddr.sin_addr, host->h_addr_list[0], host->h_length);
  maddr.sin_port = saddr.sin_port;
  if (type == SOCK_STREAM) {
	  if ((retval = listen(master_fd, 5)) < 0) {
		  return(-1);
	  }
  }
  return(master_fd);
} /* sock_master() */


/*
 * sock_slave()
 * 
 * This function opens the slave half of a Unix socket.
 */
static int sock_slave(type)
  int		type;
{
  int		slave_fd;
  int		alen;

  if ((slave_fd = socket(AF_INET, type, 0)) < 0) {
    return(-1);
  }

  alen = sizeof(saddr);
  if (connect(slave_fd, &saddr, alen) < 0) {
    close(slave_fd);
    return(-1);
  }
  if (getsockname(slave_fd, &saddr, &alen) < 0) {
    close(slave_fd);
    return(-1);
  }
  return(slave_fd);
} /* sock_slave() */





----- End Included Message -----


------------------------------------------------------------------------------