
Larry,

I have to get back to work on my day job, but I have revamped 
lat_select.c so it does a reasonable job on the load case.  
However, it is not finished.  I have found an interesting
problem: baseline != unloaded when run on my workstation.  I
would expect baseline == unloaded because:
	unloaded = BENCH(..., 0)
	baseline = BENCH(..., 100000)
output from my workstation:
	hplchs.hpl.hp.com-476> ../bin/hppa1.1-hp-hpux10.20/lat_select 
	benchmark
	baseline
	baseline: n = 104, unloaded = 966.0000, baseline = 1072.3462
	[pid=23375      n=0     u=1077461286]
	[pid=23376      n=0     u=1077461926]
	[pid=23375      n=104   u=1078056421]
	[pid=23376      n=104   u=1078361120]
		2       2004.6106       1072.3462       352.5000
	[pid=23377      n=0     u=1079416691]
	[pid=23375      n=0     u=1079417338]
	[pid=23376      n=0     u=1079425534]
	[pid=23377      n=104   u=1080138330]
	[pid=23376      n=104   u=1081058602]
	[pid=23375      n=104   u=1081167323]
	        3       3690.4808       1072.3462       2975.3333
	Select on 200 fd's: 966.0000 microseconds / 3 load
	hplchs.hpl.hp.com-477> 

Carl

/*
 * lat_select.c - time select system call
 *
 * usage: lat_select [n]
 *
 * Copyright (c) 1996 Larry McVoy.  Distributed under the FSF GPL with
 * additional restriction that results may published only if
 * (1) the benchmark is unmodified, and
 * (2) the version in the sccsid below is included in the report.
 */
char	*id = "$Id: lat_select.c,v 1.6 1997/06/16 05:38:58 lm Exp staelin $\n";

#include "bench.h"

fd_set	set;
int	n_fds;

#define	BARRIER_CTL	0
#define	BARRIER_ACK	1
#define	BENCH_CTL	2
#define	BENCH_ACK	3
int	pipes[4][2];

#define MAX_LOAD	10
int	procs;
int	pids[MAX_LOAD];

void	settle();
void	killem();
void	child_doit();
void	create_child();
double	benchmark();

void
initialize(int N)
{
	int	i;
	/* set up communication with children for load tests */
	for (i = 0; i < 4; ++i) {
		if (pipe(pipes[i]) == -1) {
			perror("pipe");
			exit(1);
		}
	}
	/* initialize fd_set for later use by tests */
	FD_ZERO(&set);
	for (i = 0; i < N; i++) {
		int fd = open("/dev/tty", 1);
		if (fd == -1) break;
		FD_SET(fd, &set);
	}
	n_fds = i;
}

void
doit(int n, fd_set *set)
{
	fd_set	save = *set;
	select(n, 0, set, 0, 0);
	*set = save;
}

void
settle()
{
	BENCH1(doit(n_fds, &set), 100000);
}

void
poll(int fd)
{
	fd_set	s;
	struct timeval tv;
	FD_ZERO(&s);
	tv.tv_sec = 0;
	tv.tv_usec = 0;
	/* has the master sent us a message yet? */
	for (;;) {
		FD_SET(fd, &s);
		select(n_fds, &s, 0, 0, &tv);
		if (FD_ISSET(fd, &s)) break;
		/* do some more work */
		settle();
	}
}

double
benchmark(int enough)
{
	BENCH(doit(n_fds, &set), enough);
	return (double)usecs_spent() / (double)get_n();
}

void
child_doit()
{
	int	c;
	int	n;
	uint64	u = 0;
int	p = getpid();
int _n;

	for (;;) {
		poll(pipes[BARRIER_CTL][0]);	/* keep working until ready */
		settle();			/* give scheduler a chance */
		if (read(pipes[BARRIER_CTL][0], &n, sizeof(n)) != sizeof(n) ||
		    write(pipes[BARRIER_ACK][1], &u, sizeof(u)) != sizeof(u)) {
			perror("read/write error on pipe");
			exit(1);
		}

		/* everyone waits for the starting gun */
		if (read(pipes[BENCH_CTL][0], &n, sizeof(n)) != sizeof(n)) {
			perror("read error on pipe");
			exit(1);
		}
		if (n < 0) exit(0);
		/* take measurements */
_n=n;
		while (n-- > 0) {
			doit(n_fds, &set);
		}
		u = now();
		u -= t_overhead();
fprintf(stderr, "[pid=%u\tn=%d\tu=%lu]\n", p, _n, u);
		if (write(pipes[BENCH_ACK][1], &u, sizeof(u)) != sizeof(u)) {
			perror("write error on pipe");
			exit(1);
		}
	}
}

void
create_child()
{
	int	c = 1;
	int	p;

	switch (p = fork()) {
	case -1:
		perror("fork");
		killem();
		exit(1);

	case 0:	/* child */
		child_doit();
		/* NOTREACHED */

	default:
		pids[procs++] = p;
		;
	}	
}

void
killem()
{
	int	i;

	for (i = 0; i < procs; ++i) {
		if (pids[i] > 0) {
			kill(pids[i], SIGTERM);
		}
	}
}

/*
 * pass a start token to procs
 */
void
fire(int procs, int c, int fd)
{
	int	i, bytes = procs * sizeof(int), *tmp = (int *)malloc(bytes);
	for (i = 0; i < procs; ++i) tmp[i] = c;
	
	if (write(fd, tmp, bytes) != bytes) {
		perror("write error on pipe");
		killem();
		exit(1);
	}
	free(tmp);
}

/*
 * collect end tokens from procs
 */
uint64
collect(int procs, int fd)
{
	uint64	u = 0, t;
	int	i;
	for (i = 0; i < procs; ++i) {
		if (read(fd, &t, sizeof(t)) != sizeof(t)) {
			perror("write error on pipe");
			killem();
			exit(1);
		}
		if (u < t) u = t;	/* pick max */
	}
	return u;
}

double
cost(int load, int n)
{
	uint64	s, u;
	double	c;

	/* start children running benchmark */
	fire(load, n, pipes[BARRIER_CTL][1]);
	collect(load, pipes[BARRIER_ACK][0]);
	s = now();
	fire(load, n, pipes[BENCH_CTL][1]);
	u = collect(load, pipes[BENCH_ACK][0]);
	c = u - s;
	c /= load;
	return c;
}

#define MAX(a, b) ((a) > (b) ? (a) : (b))
int
main(int ac, char **av)
{
	char	buf[256];
	int	n;
	int	load;
	int	N = ac > 1 ? atoi(av[1]) : 200;
	int	enough = get_enough(0);
	uint64	s, u;
	double	baseline, unloaded, loaded, overhead;

	morefds();
	initialize(N);
fprintf(stderr, "benchmark\n");
	unloaded = benchmark(enough);
fprintf(stderr, "baseline\n");
	baseline = benchmark(MAX(100000, enough));
	n = get_n();
fprintf(stderr, "baseline: n = %d, unloaded = %.4f, baseline = %.4f\n", n, 
unloaded, baseline);
	for (load = 1, loaded = baseline; load < MAX_LOAD; ++load) {
		create_child();
		if (load < 2) continue;
		overhead = cost(load, 0);
		loaded = cost(load, n);
		loaded -= overhead;
		loaded /= n;
fprintf(stderr, "\t%d\t%.4f\t%.4f\t%.4f\n", load, loaded, baseline, overhead);
		if (loaded >= 2. * baseline) break;
	}
	printf("Select on %d fd's: %.4f microseconds / %d load\n", 
	       N, unloaded, load);
	killem();
	return(0);
}

-- 
-------------------------------------------------------------------------------
Carl Staelin				
  email:	staelin@hpl.hp.com		Hewlett-Packard Laboratories
  voice:	(415)857-6823			1501 Page Mill Road, M/S 3L-9
  FAX:		(415)852-2986			P.O. Box 10490
  http://www.hpl.hp.com/personal/Carl_Staelin	Palo Alto, CA 94303-0969
-------------------------------------------------------------------------------

