2 simple thread/process benchmark
4 Copyright (C) Andrew Tridgell <tridge@samba.org> 2003
6 Released under the GNU GPL version 2 or later
10 this program is designed to test the relative performance of threads/processes
11 for operations typically performed by fileserving applications.
19 #include <sys/types.h>
32 /* this contains per-task data */
38 /* these pipes are used for synchronised startup of the tasks */
39 static struct barrier {
45 static void barrier_setup(struct barrier *b)
47 if (pipe(b->fd1) != 0 || pipe(b->fd2) != 0) {
48 fprintf(stderr,"Barrier setup failed\n");
53 /* cleanup the barrier pipes */
54 static void barrier_cleanup(struct barrier *b)
62 /* wait for the parent to signal startup */
63 static void barrier_wait(struct barrier *b)
67 if (write(b->fd1[1], &c, 1) != 1 ||
68 read(b->fd2[0], &c, 1) != 1) {
69 fprintf(stderr, "Barrier wait failed\n");
74 /* synchronise children. Return the amount of time since the last
76 static double barrier_parent(struct barrier *b, int nprocs)
78 char *s = calloc(nprocs, 1);
82 static struct timeval tp1;
85 for (i=0;i<nprocs;i++) {
86 while (read(b->fd1[0], &c, 1) != 1) ;
89 /* putting the timer here prevents problems with the parent getting
90 rescheduled after the write */
91 gettimeofday(&tp2,NULL);
92 t = (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) -
93 (tp1.tv_sec + (tp1.tv_usec*1.0e-6));
94 gettimeofday(&tp1,NULL);
96 while (nwritten != nprocs) {
97 int n = write(b->fd2[1], s, nprocs-nwritten);
99 fprintf(stderr, "Barrier parent failed\n");
110 create a thread with initial function fn(private)
112 static pthread_t thread_start(void *(*fn)(int), int id)
115 pthread_attr_t thread_attr;
117 typedef void *(*thread_fn_t)(void *);
118 pthread_attr_init(&thread_attr);
119 pthread_attr_setdetachstate(&thread_attr, 0);
120 rc = pthread_create(&thread_id, &thread_attr, (thread_fn_t)fn, (void *)id);
121 pthread_attr_destroy(&thread_attr);
124 fprintf(stderr,"Thread create failed for id %d\n", id);
131 /* wait for a thread to exit */
132 static int thread_join(pthread_t id)
134 return pthread_join(id, NULL);
140 create a process with initial function fn(private)
142 static pid_t proc_start(void *(*fn)(int), int id)
147 if (pid == (pid_t)-1) {
148 fprintf(stderr,"Fork failed for id %d\n", id);
158 /* wait for a process to exit */
159 static int proc_join(pid_t id)
161 if (waitpid(id, NULL, 0) != id) {
168 /* run a function under a set of threads */
169 static double run_threads(int nthreads, void *(*fn)(int ))
172 pthread_t *ids = calloc(sizeof(*ids), nthreads);
175 barrier_setup(&barriers[0]);
176 barrier_setup(&barriers[1]);
178 for (i=0;i<nthreads;i++) {
179 ids[i] = thread_start(fn, i);
182 barrier_parent(&barriers[0], nthreads);
183 t = barrier_parent(&barriers[1], nthreads);
185 for (i=0;i<nthreads;i++) {
187 rc = thread_join(ids[i]);
189 fprintf(stderr, "Thread %d failed : %s\n", i, strerror(errno));
194 barrier_cleanup(&barriers[0]);
195 barrier_cleanup(&barriers[1]);
203 /* run a function under a set of processes */
204 static double run_processes(int nprocs, void *(*fn)(int ))
207 pid_t *ids = calloc(sizeof(*ids), nprocs);
210 barrier_setup(&barriers[0]);
211 barrier_setup(&barriers[1]);
213 for (i=0;i<nprocs;i++) {
214 ids[i] = proc_start(fn, i);
215 if (ids[i] == (pid_t)-1) {
217 kill(ids[i], SIGTERM);
223 barrier_parent(&barriers[0], nprocs);
224 t = barrier_parent(&barriers[1], nprocs);
226 for (i=0;i<nprocs;i++) {
228 rc = proc_join(ids[i]);
230 fprintf(stderr, "Process %d failed : %s\n", i, strerror(errno));
235 barrier_cleanup(&barriers[0]);
236 barrier_cleanup(&barriers[1]);
245 /***********************************************************************
246 a simple malloc speed test using a wide variety of malloc sizes
247 ************************************************************************/
248 static void *test_malloc(int id)
252 void *ptrs[NMALLOCS];
254 barrier_wait(&barriers[0]);
256 for (j=0;j<500;j++) {
257 for (i=1;i<NMALLOCS;i++) {
260 printf("malloc(%d) failed!\n", i);
264 for (i=1;i<NMALLOCS;i++) {
269 barrier_wait(&barriers[1]);
274 /***********************************************************************
275 simple read/write testing using /dev/null and /dev/zero
276 ************************************************************************/
277 static void *test_readwrite(int id)
281 /* we use less than 1 page to prevent page table games */
284 barrier_wait(&barriers[0]);
286 fd_in = open("/dev/zero", O_RDONLY);
287 fd_out = open("/dev/null", O_WRONLY);
288 if (fd_in == -1 || fd_out == -1) {
289 fprintf(stderr,"Failed to open /dev/zero or /dev/null\n");
293 for (i=0;i<20000;i++) {
294 if (read(fd_in, buf, sizeof(buf)) != sizeof(buf) ||
295 write(fd_out, buf, sizeof(buf)) != sizeof(buf)) {
296 fprintf(stderr,"IO failed at loop %d\n", i);
304 barrier_wait(&barriers[1]);
310 /***********************************************************************
311 test stat() operations
312 ************************************************************************/
313 static void *test_stat(int id)
317 barrier_wait(&barriers[0]);
319 for (i=0;i<30000;i++) {
321 if (stat(id_data[id].dname, &st) != 0) goto failed;
322 if (stat(id_data[id].fname, &st) == 0) goto failed;
325 barrier_wait(&barriers[1]);
330 fprintf(stderr,"stat failed\n");
334 /***********************************************************************
335 test fstat() operations
336 ************************************************************************/
337 static void *test_fstat(int id)
341 barrier_wait(&barriers[0]);
343 fd = open(id_data[id].fname, O_RDWR|O_CREAT, 0600);
344 if (fd == -1) goto failed;
346 for (i=0;i<1000000;i++) {
348 if (fstat(fd, &st) != 0) goto failed;
352 unlink(id_data[id].fname);
354 barrier_wait(&barriers[1]);
359 fprintf(stderr,"fstat failed\n");
363 /***********************************************************************
364 test directory operations
365 ************************************************************************/
366 static void *test_dir(int id)
370 barrier_wait(&barriers[0]);
372 for (i=0;i<2000;i++) {
373 DIR *d = opendir(id_data[id].dname);
375 while (readdir(d)) {} ;
379 barrier_wait(&barriers[1]);
383 fprintf(stderr,"dir failed\n");
387 /***********************************************************************
388 test directory operations
389 ************************************************************************/
390 static void *test_dirsingle(int id)
394 barrier_wait(&barriers[0]);
396 for (i=0;i<2000;i++) {
397 DIR *d = opendir(".");
399 while (readdir(d)) {} ;
403 barrier_wait(&barriers[1]);
407 fprintf(stderr,"dirsingle failed\n");
412 /***********************************************************************
413 test create/unlink operations
414 ************************************************************************/
415 static void *test_create(int id)
419 barrier_wait(&barriers[0]);
421 for (i=0;i<3000;i++) {
423 fd = open(id_data[id].fname, O_CREAT|O_TRUNC|O_RDWR, 0666);
424 if (fd == -1) goto failed;
425 if (open(id_data[id].fname, O_CREAT|O_TRUNC|O_RDWR|O_EXCL, 0666) != -1) goto failed;
427 if (unlink(id_data[id].fname) != 0) goto failed;
430 barrier_wait(&barriers[1]);
434 fprintf(stderr,"create failed\n");
439 /***********************************************************************
440 test fcntl lock operations
441 ************************************************************************/
442 static void *test_lock(int id)
447 barrier_wait(&barriers[0]);
449 fd = open(id_data[id].fname, O_CREAT|O_RDWR, 0666);
450 if (fd == -1) goto failed;
451 unlink(id_data[id].fname);
453 for (i=0;i<20000;i++) {
455 lock.l_type = F_WRLCK;
456 lock.l_whence = SEEK_SET;
457 lock.l_start = (id*100) + (i%100);
461 if (fcntl(fd,F_SETLK,&lock) != 0) goto failed;
463 lock.l_type = F_UNLCK;
465 if (fcntl(fd,F_SETLK,&lock) != 0) goto failed;
470 barrier_wait(&barriers[1]);
474 fprintf(stderr,"lock failed\n");
478 /***********************************************************************
480 ************************************************************************/
481 static void *test_noop(int id)
483 barrier_wait(&barriers[0]);
484 barrier_wait(&barriers[1]);
490 show the average and range of a set of results
492 static void show_result(const char *name, double *t, int nrepeats)
494 double mint, maxt, total;
496 total = mint = maxt = t[0];
497 for (i=1;i<nrepeats;i++) {
498 if (t[i] < mint) mint = t[i];
499 if (t[i] > maxt) maxt = t[i];
502 printf("%s %5.2f +/- %.2f seconds\n", name, total/nrepeats, (maxt-mint)/2);
506 /* lock a byte range in a open file */
507 int main(int argc, char *argv[])
517 {"malloc", test_malloc},
518 {"readwrite", test_readwrite},
520 {"fstat", test_fstat},
522 {"dirsingle", test_dirsingle},
523 {"create", test_create},
529 printf("thread_perf NPROCS\n");
533 nprocs = atoi(argv[1]);
539 id_data = calloc(nprocs, sizeof(*id_data));
545 printf("NOTE! for accurate process results please compile with -DNO_THREADS and don't link to -lpthread\n\n");
548 for (i=0;i<nprocs;i++) {
550 sprintf(s, "testd_%d", i);
551 id_data[i].dname = strdup(s);
553 sprintf(s, "%s/test.dat", id_data[i].dname);
554 id_data[i].fname = strdup(s);
556 rmdir(id_data[i].dname);
557 if (mkdir(id_data[i].dname, 0777) != 0) {
558 fprintf(stderr, "Failed to create %s\n", id_data[i].dname);
562 unlink(id_data[i].fname);
565 for (i=0;tests[i].name;i++) {
566 double t_threads[NREPEATS];
567 double t_processes[NREPEATS];
570 if (strcasecmp(tname, "ALL") && strcasecmp(tests[i].name, tname)) {
574 printf("Running test '%s' with %d tasks\n", tests[i].name, nprocs);
576 for (j=0;j<NREPEATS;j++) {
578 t_threads[j] = run_threads(nprocs, tests[i].fn);
580 t_processes[j] = run_processes(nprocs, tests[i].fn);
583 show_result("Threads ", t_threads, NREPEATS);
585 show_result("Processes", t_processes, NREPEATS);
591 for (i=0;i<nprocs;i++) {
592 if (rmdir(id_data[i].dname) != 0) {
593 fprintf(stderr, "Failed to delete %s\n", id_data[i].dname);
598 for (i=0;i<nprocs;i++) {
599 free(id_data[i].dname);
600 free(id_data[i].fname);