.\" Copyright (c) 1985 Regents of the University of California.
.\" All rights reserved.  The Berkeley software License Agreement
.\" specifies the terms and conditions for redistribution.
.\"
.\"	@(#)a1.t	1.1 (Berkeley) 7/27/85
.\"
.ds RH Appendix A \- Benchmark sources
.nr H2 1
.sp 2
.de vS
.nf
..
.de vE
.fi
..
.bp
.SH
\s+2Appendix A \- Benchmark sources\s-2
.LP
The programs shown here run under 4.2 with only routines
from the standard libraries.  When run under 4.1 they were augmented
with a \fIgetpagesize\fP routine and a copy of the \fIrandom\fP
function from the C library.  The \fIvforks\fP and \fIvexecs\fP
programs are constructed from the \fIforks\fP and \fIexecs\fP programs,
respectively, by substituting calls to \fIfork\fP with calls to
\fIvfork\fP.
.SH
syscall
.LP
.vS
/*
 * System call overhead benchmark.
 */
main(argc, argv)
	char *argv[];
{
	register int ncalls;

	if (argc < 2) {
		printf("usage: %s #syscalls\n", argv[0]);
		exit(1);
	}
	ncalls = atoi(argv[1]);
	while (ncalls-- > 0)
		(void) getpid();
}
.vE
.SH
csw
.LP
.vS
/*
 * Context switching benchmark.
 *
 * Force system to context switch 2*nsigs
 * times by forking and exchanging signals.
 * To calculate system overhead for a context
 * switch, the signocsw program must be run
 * with nsigs.  Overhead is then estimated by
 *	t1 = time csw <n>
 *	t2 = time signocsw <n>
 *	overhead = t1 - 2 * t2;
 */
#include <signal.h>

int	sigsub();
int	otherpid;
int	nsigs;

main(argc, argv)
	char *argv[];
{
	int pid;

	if (argc < 2) {
		printf("usage: %s nsignals\n", argv[0]);
		exit(1);
	}
	nsigs = atoi(argv[1]);
	signal(SIGALRM, sigsub);
	otherpid = getpid();
	pid = fork();
	if (pid != 0) {
		otherpid = pid;
		kill(otherpid, SIGALRM);
	}
	for (;;)
		sigpause(0);
}

sigsub()
{

	signal(SIGALRM, sigsub);
	kill(otherpid, SIGALRM);
	if (--nsigs <= 0)
		exit(0);
}
.vE
.SH
signocsw
.LP
.vS
/*
 * Signal without context switch benchmark.
 */
#include <signal.h>

int	pid;
int	nsigs;
int	sigsub();

main(argc, argv)
	char *argv[];
{
	register int i;

	if (argc < 2) {
		printf("usage: %s nsignals\n", argv[0]);
		exit(1);
	}
	nsigs = atoi(argv[1]);
	signal(SIGALRM, sigsub);
	pid = getpid();
	for (i = 0; i < nsigs; i++)
		kill(pid, SIGALRM);
}

sigsub()
{

	signal(SIGALRM, sigsub);
}
.vE
.SH
pipeself
.LP
.vS
/*
 * IPC benchmark,
 * write to self using pipes.
 */

main(argc, argv)
	char *argv[];
{
	char buf[512];
	int fd[2], msgsize;
	register int i, iter;

	if (argc < 3) {
		printf("usage: %s iterations message-size\n", argv[0]);
		exit(1);
	}
	argc--, argv++;
	iter = atoi(*argv);
	argc--, argv++;
	msgsize = atoi(*argv);
	if (msgsize > sizeof (buf) || msgsize <= 0) {
		printf("%s: Bad message size.\n", *argv);
		exit(2);
	}
	if (pipe(fd) < 0) {
		perror("pipe");
		exit(3);
	}
	for (i = 0; i < iter; i++) {
		write(fd[1], buf, msgsize);
		read(fd[0], buf, msgsize);
	}
}
.vE
.SH
pipediscard
.LP
.vS
/*
 * IPC benchmarkl,
 * write and discard using pipes.
 */

main(argc, argv)
	char *argv[];
{
	char buf[512];
	int fd[2], msgsize;
	register int i, iter;

	if (argc < 3) {
		printf("usage: %s iterations message-size\n", argv[0]);
		exit(1);
	}
	argc--, argv++;
	iter = atoi(*argv);
	argc--, argv++;
	msgsize = atoi(*argv);
	if (msgsize > sizeof (buf) || msgsize <= 0) {
		printf("%s: Bad message size.\n", *argv);
		exit(2);
	}
	if (pipe(fd) < 0) {
		perror("pipe");
		exit(3);
	}
	if (fork() == 0)
		for (i = 0; i < iter; i++)
			read(fd[0], buf, msgsize);
	else
		for (i = 0; i < iter; i++)
			write(fd[1], buf, msgsize);
}
.vE
.SH
pipeback
.LP
.vS
/*
 * IPC benchmark,
 * read and reply using pipes.
 *
 * Process forks and exchanges messages
 * over a pipe in a request-response fashion.
 */

main(argc, argv)
	char *argv[];
{
	char buf[512];
	int fd[2], fd2[2], msgsize;
	register int i, iter;

	if (argc < 3) {
		printf("usage: %s iterations message-size\n", argv[0]);
		exit(1);
	}
	argc--, argv++;
	iter = atoi(*argv);
	argc--, argv++;
	msgsize = atoi(*argv);
	if (msgsize > sizeof (buf) || msgsize <= 0) {
		printf("%s: Bad message size.\n", *argv);
		exit(2);
	}
	if (pipe(fd) < 0) {
		perror("pipe");
		exit(3);
	}
	if (pipe(fd2) < 0) {
		perror("pipe");
		exit(3);
	}
	if (fork() == 0)
		for (i = 0; i < iter; i++) {
			read(fd[0], buf, msgsize);
			write(fd2[1], buf, msgsize);
		}
	else
		for (i = 0; i < iter; i++) {
			write(fd[1], buf, msgsize);
			read(fd2[0], buf, msgsize);
		}
}
.vE
.SH
forks
.LP
.vS
/*
 * Benchmark program to calculate fork+wait
 * overhead (approximately).  Process
 * forks and exits while parent waits.
 * The time to run this program is used
 * in calculating exec overhead.
 */

main(argc, argv)
	char *argv[];
{
	register int nforks, i;
	char *cp;
	int pid, child, status, brksize;

	if (argc < 2) {
		printf("usage: %s number-of-forks sbrk-size\n", argv[0]);
		exit(1);
	}
	nforks = atoi(argv[1]);
	if (nforks < 0) {
		printf("%s: bad number of forks\n", argv[1]);
		exit(2);
	}
	brksize = atoi(argv[2]);
	if (brksize < 0) {
		printf("%s: bad size to sbrk\n", argv[2]);
		exit(3);
	}
	cp = (char *)sbrk(brksize);
	if ((int)cp == -1) {
		perror("sbrk");
		exit(4);
	}
	for (i = 0; i < brksize; i += 1024)
		cp[i] = i;
	while (nforks-- > 0) {
		child = fork();
		if (child == -1) {
			perror("fork");
			exit(-1);
		}
		if (child == 0)
			_exit(-1);
		while ((pid = wait(&status)) != -1 && pid != child)
			;
	}
	exit(0);
}
.vE
.SH
execs
.LP
.vS
/*
 * Benchmark program to calculate exec
 * overhead (approximately).  Process
 * forks and execs "null" test program.
 * The time to run the fork program should
 * then be deducted from this one to
 * estimate the overhead for the exec.
 */

main(argc, argv)
	char *argv[];
{
	register int nexecs, i;
	char *cp, *sbrk();
	int pid, child, status, brksize;

	if (argc < 3) {
		printf("usage: %s number-of-execs sbrk-size job-name\n",
		    argv[0]);
		exit(1);
	}
	nexecs = atoi(argv[1]);
	if (nexecs < 0) {
		printf("%s: bad number of execs\n", argv[1]);
		exit(2);
	}
	brksize = atoi(argv[2]);
	if (brksize < 0) {
		printf("%s: bad size to sbrk\n", argv[2]);
		exit(3);
	}
	cp = sbrk(brksize);
	if ((int)cp == -1) {
		perror("sbrk");
		exit(4);
	}
	for (i = 0; i < brksize; i += 1024)
		cp[i] = i;
	while (nexecs-- > 0) {
		child = fork();
		if (child == -1) {
			perror("fork");
			exit(-1);
		}
		if (child == 0) {
			execv(argv[3], argv);
			perror("execv");
			_exit(-1);
		}
		while ((pid = wait(&status)) != -1 && pid != child)
			;
	}
	exit(0);
}
.vE
.SH
nulljob
.LP
.vS
/*
 * Benchmark "null job" program.
 */

main(argc, argv)
	char *argv[];
{

	exit(0);
}
.vE
.SH
bigjob
.LP
.vS
/*
 * Benchmark "null big job" program.
 */
/* 250 here is intended to approximate vi's text+data size */
char	space[1024 * 250] = "force into data segment";

main(argc, argv)
	char *argv[];
{

	exit(0);
}
.vE
.bp
.SH
seqpage
.LP
.vS
/*
 * Sequential page access benchmark.
 */
#include <sys/vadvise.h>

char	*valloc();

main(argc, argv)
	char *argv[];
{
	register i, niter;
	register char *pf, *lastpage;
	int npages = 4096, pagesize, vflag = 0;
	char *pages, *name;

	name = argv[0];
	argc--, argv++;
again:
	if (argc < 1) {
usage:
		printf("usage: %s [ -v ] [ -p #pages ] niter\n", name);
		exit(1);
	}
	if (strcmp(*argv, "-p") == 0) {
		argc--, argv++;
		if (argc < 1)
			goto usage;
		npages = atoi(*argv);
		if (npages <= 0) {
			printf("%s: Bad page count.\n", *argv);
			exit(2);
		}
		argc--, argv++;
		goto again;
	}
	if (strcmp(*argv, "-v") == 0) {
		argc--, argv++;
		vflag++;
		goto again;
	}
	niter = atoi(*argv);
	pagesize = getpagesize();
	pages = valloc(npages * pagesize);
	if (pages == (char *)0) {
		printf("Can't allocate %d pages (%2.1f megabytes).\n",
		    npages, (npages * pagesize) / (1024. * 1024.));
		exit(3);
	}
	lastpage = pages + (npages * pagesize);
	if (vflag)
		vadvise(VA_SEQL);
	for (i = 0; i < niter; i++)
		for (pf = pages; pf < lastpage; pf += pagesize)
			*pf = 1;
}
.vE
.SH
randpage
.LP
.vS
/*
 * Random page access benchmark.
 */
#include <sys/vadvise.h>

char	*valloc();
int	rand();

main(argc, argv)
	char *argv[];
{
	register int npages = 4096, pagesize, pn, i, niter;
	int vflag = 0, debug = 0;
	char *pages, *name;

	name = argv[0];
	argc--, argv++;
again:
	if (argc < 1) {
usage:
		printf("usage: %s [ -d ] [ -v ] [ -p #pages ] niter\n", name);
		exit(1);
	}
	if (strcmp(*argv, "-p") == 0) {
		argc--, argv++;
		if (argc < 1)
			goto usage;
		npages = atoi(*argv);
		if (npages <= 0) {
			printf("%s: Bad page count.\n", *argv);
			exit(2);
		}
		argc--, argv++;
		goto again;
	}
	if (strcmp(*argv, "-v") == 0) {
		argc--, argv++;
		vflag++;
		goto again;
	}
	if (strcmp(*argv, "-d") == 0) {
		argc--, argv++;
		debug++;
		goto again;
	}
	niter = atoi(*argv);
	pagesize = getpagesize();
	pages = valloc(npages * pagesize);
	if (pages == (char *)0) {
		printf("Can't allocate %d pages (%2.1f megabytes).\n",
		    npages, (npages * pagesize) / (1024. * 1024.));
		exit(3);
	}
	if (vflag)
		vadvise(VA_ANOM);
	for (i = 0; i < niter; i++) {
		pn = random() % npages;
		if (debug)
			printf("touch page %d\n", pn);
		pages[pagesize * pn] = 1;
	}
}
.vE
.SH
gausspage
.LP
.vS
/*
 * Random page access with
 * a gaussian distribution.
 *
 * Allocate a large (zero fill on demand) address
 * space and fault the pages in a random gaussian
 * order.
 */

float	sqrt(), log(), rnd(), cos(), gauss();
char	*valloc();
int	rand();

main(argc, argv)
	char *argv[];
{
	register int pn, i, niter, delta;
	register char *pages;
	float sd = 10.0;
	int npages = 4096, pagesize, debug = 0;
	char *name;

	name = argv[0];
	argc--, argv++;
again:
	if (argc < 1) {
usage:
		printf(
"usage: %s [ -d ] [ -p #pages ] [ -s standard-deviation ] iterations\n", name);
		exit(1);
	}
	if (strcmp(*argv, "-s") == 0) {
		argc--, argv++;
		if (argc < 1)
			goto usage;
		sscanf(*argv, "%f", &sd);
		if (sd <= 0) {
			printf("%s: Bad standard deviation.\n", *argv);
			exit(2);
		}
		argc--, argv++;
		goto again;
	}
	if (strcmp(*argv, "-p") == 0) {
		argc--, argv++;
		if (argc < 1)
			goto usage;
		npages = atoi(*argv);
		if (npages <= 0) {
			printf("%s: Bad page count.\n", *argv);
			exit(2);
		}
		argc--, argv++;
		goto again;
	}
	if (strcmp(*argv, "-d") == 0) {
		argc--, argv++;
		debug++;
		goto again;
	}
	niter = atoi(*argv);
	pagesize = getpagesize();
	pages = valloc(npages*pagesize);
	if (pages == (char *)0) {
		printf("Can't allocate %d pages (%2.1f megabytes).\n",
		    npages, (npages*pagesize) / (1024. * 1024.));
		exit(3);
	}
	pn = 0;
	for (i = 0; i < niter; i++) {
		delta = gauss(sd, 0.0);
		while (pn + delta < 0 || pn + delta > npages)
			delta = gauss(sd, 0.0);
		pn += delta;
		if (debug)
			printf("touch page %d\n", pn);
		else
			pages[pn * pagesize] = 1;
	}
}

float
gauss(sd, mean)
	float sd, mean;
{
	register float qa, qb;

	qa = sqrt(log(rnd()) * -2.0);
	qb = 3.14159 * rnd();
	return (qa * cos(qb) * sd + mean);
}

float
rnd()
{
	static int seed = 1;
	static int biggest = 0x7fffffff;

	return ((float)rand(seed) / (float)biggest);
}
.vE
