/* simple thread/process benchmark Copyright (C) Andrew Tridgell 2003 Released under the GNU GPL version 2 or later */ /* this program is designed to test the relative performance of threads/processes for operations typically performed by fileserving applications. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NO_THREADS #include #endif /* this contains per-task data */ static struct { char *dname; char *fname; } *id_data; /* these pipes are used for synchronised startup of the tasks */ static struct barrier { int fd1[2]; int fd2[2]; } barriers[2]; /* setup a barrier */ static void barrier_setup(struct barrier *b) { if (pipe(b->fd1) != 0 || pipe(b->fd2) != 0) { fprintf(stderr,"Barrier setup failed\n"); exit(1); } } /* cleanup the barrier pipes */ static void barrier_cleanup(struct barrier *b) { close(b->fd1[0]); close(b->fd1[1]); close(b->fd2[0]); close(b->fd2[1]); } /* wait for the parent to signal startup */ static void barrier_wait(struct barrier *b) { char c = 0; if (write(b->fd1[1], &c, 1) != 1 || read(b->fd2[0], &c, 1) != 1) { fprintf(stderr, "Barrier wait failed\n"); exit(1); } } /* synchronise children. Return the amount of time since the last barrier */ static double barrier_parent(struct barrier *b, int nprocs) { char *s = calloc(nprocs, 1); int i, nwritten=0; char c = 0; double t; static struct timeval tp1; struct timeval tp2; for (i=0;ifd1[0], &c, 1) != 1) ; } /* putting the timer here prevents problems with the parent getting rescheduled after the write */ gettimeofday(&tp2,NULL); t = (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) - (tp1.tv_sec + (tp1.tv_usec*1.0e-6)); gettimeofday(&tp1,NULL); while (nwritten != nprocs) { int n = write(b->fd2[1], s, nprocs-nwritten); if (n <= 0) { fprintf(stderr, "Barrier parent failed\n"); exit(1); } nwritten += n; } free(s); return t; } #ifndef NO_THREADS /* create a thread with initial function fn(private) */ static pthread_t thread_start(void *(*fn)(int), int id) { pthread_t thread_id; pthread_attr_t thread_attr; int rc; typedef void *(*thread_fn_t)(void *); pthread_attr_init(&thread_attr); pthread_attr_setdetachstate(&thread_attr, 0); rc = pthread_create(&thread_id, &thread_attr, (thread_fn_t)fn, (void *)(intptr_t)id); pthread_attr_destroy(&thread_attr); if (rc != 0) { fprintf(stderr,"Thread create failed for id %d\n", id); exit(1); } return thread_id; } /* wait for a thread to exit */ static int thread_join(pthread_t id) { return pthread_join(id, NULL); } #endif /* create a process with initial function fn(private) */ static pid_t proc_start(void *(*fn)(int), int id) { pid_t pid; pid = fork(); if (pid == (pid_t)-1) { fprintf(stderr,"Fork failed for id %d\n", id); return pid; } if (pid == 0) { fn(id); exit(0); } return pid; } /* wait for a process to exit */ static int proc_join(pid_t id) { if (waitpid(id, NULL, 0) != id) { return -1; } return 0; } #ifndef NO_THREADS /* run a function under a set of threads */ static double run_threads(int nthreads, void *(*fn)(int )) { int i; pthread_t *ids = calloc(sizeof(*ids), nthreads); double t; barrier_setup(&barriers[0]); barrier_setup(&barriers[1]); for (i=0;i=0;i--) { kill(ids[i], SIGTERM); } exit(1); } } barrier_parent(&barriers[0], nprocs); t = barrier_parent(&barriers[1], nprocs); for (i=0;i maxt) maxt = t[i]; total += t[i]; } printf("%s %5.2f +/- %.2f seconds\n", name, total/nrepeats, (maxt-mint)/2); } /* lock a byte range in a open file */ int main(int argc, char *argv[]) { int nprocs, i; char *tname = "ALL"; #define NREPEATS 10 struct { const char *name; void *(*fn)(int ); } tests[] = { {"noop", test_noop}, {"malloc", test_malloc}, {"setreuid", test_setreuid}, {"readwrite", test_readwrite}, {"stat", test_stat}, {"fstat", test_fstat}, {"dir", test_dir}, {"dirsingle", test_dirsingle}, {"create", test_create}, {"lock", test_lock}, {NULL, NULL} }; if (argc <= 1) { printf("thread_perf NPROCS\n"); exit(1); } nprocs = atoi(argv[1]); if (argc > 2) { tname = argv[2]; } id_data = calloc(nprocs, sizeof(*id_data)); if (!id_data) { exit(1); } #ifndef NO_THREADS printf("NOTE! for accurate process results please compile with -DNO_THREADS and don't link to -lpthread\n\n"); #endif for (i=0;i