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 *)(intptr_t)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]);
273 /***********************************************************************
274 a simple setreuid speed test
275 ************************************************************************/
276 static void *test_setreuid(int id)
279 void *ptrs[NMALLOCS];
281 barrier_wait(&barriers[0]);
284 printf("Skipping setreuid test for non-root user\n");
286 barrier_wait(&barriers[1]);
291 for (i=0;i<2000;i++) {
292 if (setreuid(-1, 1) != 0 ||
293 setreuid(-1, 0) != 0) {
294 printf("setreuid failed: %s\n", strerror(errno));
295 barrier_wait(&barriers[1]);
300 barrier_wait(&barriers[1]);
305 /***********************************************************************
306 simple read/write testing using /dev/null and /dev/zero
307 ************************************************************************/
308 static void *test_readwrite(int id)
312 /* we use less than 1 page to prevent page table games */
315 barrier_wait(&barriers[0]);
317 fd_in = open("/dev/zero", O_RDONLY);
318 fd_out = open("/dev/null", O_WRONLY);
319 if (fd_in == -1 || fd_out == -1) {
320 fprintf(stderr,"Failed to open /dev/zero or /dev/null\n");
324 for (i=0;i<20000;i++) {
325 if (read(fd_in, buf, sizeof(buf)) != sizeof(buf) ||
326 write(fd_out, buf, sizeof(buf)) != sizeof(buf)) {
327 fprintf(stderr,"IO failed at loop %d\n", i);
335 barrier_wait(&barriers[1]);
341 /***********************************************************************
342 test stat() operations
343 ************************************************************************/
344 static void *test_stat(int id)
348 barrier_wait(&barriers[0]);
350 for (i=0;i<30000;i++) {
352 if (stat(id_data[id].dname, &st) != 0) goto failed;
353 if (stat(id_data[id].fname, &st) == 0) goto failed;
356 barrier_wait(&barriers[1]);
361 fprintf(stderr,"stat failed\n");
365 /***********************************************************************
366 test fstat() operations
367 ************************************************************************/
368 static void *test_fstat(int id)
372 barrier_wait(&barriers[0]);
374 fd = open(id_data[id].fname, O_RDWR|O_CREAT, 0600);
375 if (fd == -1) goto failed;
377 for (i=0;i<1000000;i++) {
379 if (fstat(fd, &st) != 0) goto failed;
383 unlink(id_data[id].fname);
385 barrier_wait(&barriers[1]);
390 fprintf(stderr,"fstat failed\n");
394 /***********************************************************************
395 test directory operations
396 ************************************************************************/
397 static void *test_dir(int id)
401 barrier_wait(&barriers[0]);
403 for (i=0;i<2000;i++) {
404 DIR *d = opendir(id_data[id].dname);
406 while (readdir(d)) {} ;
410 barrier_wait(&barriers[1]);
414 fprintf(stderr,"dir failed\n");
418 /***********************************************************************
419 test directory operations
420 ************************************************************************/
421 static void *test_dirsingle(int id)
425 barrier_wait(&barriers[0]);
427 for (i=0;i<2000;i++) {
428 DIR *d = opendir(".");
430 while (readdir(d)) {} ;
434 barrier_wait(&barriers[1]);
438 fprintf(stderr,"dirsingle failed\n");
443 /***********************************************************************
444 test create/unlink operations
445 ************************************************************************/
446 static void *test_create(int id)
450 barrier_wait(&barriers[0]);
452 for (i=0;i<3000;i++) {
454 fd = open(id_data[id].fname, O_CREAT|O_TRUNC|O_RDWR, 0666);
455 if (fd == -1) goto failed;
456 if (open(id_data[id].fname, O_CREAT|O_TRUNC|O_RDWR|O_EXCL, 0666) != -1) goto failed;
458 if (unlink(id_data[id].fname) != 0) goto failed;
461 barrier_wait(&barriers[1]);
465 fprintf(stderr,"create failed\n");
470 /***********************************************************************
471 test fcntl lock operations
472 ************************************************************************/
473 static void *test_lock(int id)
478 barrier_wait(&barriers[0]);
480 fd = open(id_data[id].fname, O_CREAT|O_RDWR, 0666);
481 if (fd == -1) goto failed;
482 unlink(id_data[id].fname);
484 for (i=0;i<20000;i++) {
486 lock.l_type = F_WRLCK;
487 lock.l_whence = SEEK_SET;
488 lock.l_start = (id*100) + (i%100);
492 if (fcntl(fd,F_SETLK,&lock) != 0) goto failed;
494 lock.l_type = F_UNLCK;
496 if (fcntl(fd,F_SETLK,&lock) != 0) goto failed;
501 barrier_wait(&barriers[1]);
505 fprintf(stderr,"lock failed\n");
509 /***********************************************************************
511 ************************************************************************/
512 static void *test_noop(int id)
514 barrier_wait(&barriers[0]);
515 barrier_wait(&barriers[1]);
521 show the average and range of a set of results
523 static void show_result(const char *name, double *t, int nrepeats)
525 double mint, maxt, total;
527 total = mint = maxt = t[0];
528 for (i=1;i<nrepeats;i++) {
529 if (t[i] < mint) mint = t[i];
530 if (t[i] > maxt) maxt = t[i];
533 printf("%s %5.2f +/- %.2f seconds\n", name, total/nrepeats, (maxt-mint)/2);
537 /* lock a byte range in a open file */
538 int main(int argc, char *argv[])
548 {"malloc", test_malloc},
549 {"setreuid", test_setreuid},
550 {"readwrite", test_readwrite},
552 {"fstat", test_fstat},
554 {"dirsingle", test_dirsingle},
555 {"create", test_create},
561 printf("thread_perf NPROCS\n");
565 nprocs = atoi(argv[1]);
571 id_data = calloc(nprocs, sizeof(*id_data));
577 printf("NOTE! for accurate process results please compile with -DNO_THREADS and don't link to -lpthread\n\n");
580 for (i=0;i<nprocs;i++) {
582 sprintf(s, "testd_%d", i);
583 id_data[i].dname = strdup(s);
585 sprintf(s, "%s/test.dat", id_data[i].dname);
586 id_data[i].fname = strdup(s);
588 rmdir(id_data[i].dname);
589 if (mkdir(id_data[i].dname, 0777) != 0) {
590 fprintf(stderr, "Failed to create %s\n", id_data[i].dname);
594 unlink(id_data[i].fname);
597 for (i=0;tests[i].name;i++) {
598 double t_threads[NREPEATS];
599 double t_processes[NREPEATS];
602 if (strcasecmp(tname, "ALL") && strcasecmp(tests[i].name, tname)) {
606 printf("Running test '%s' with %d tasks\n", tests[i].name, nprocs);
608 for (j=0;j<NREPEATS;j++) {
610 t_threads[j] = run_threads(nprocs, tests[i].fn);
612 t_processes[j] = run_processes(nprocs, tests[i].fn);
615 show_result("Threads ", t_threads, NREPEATS);
617 show_result("Processes", t_processes, NREPEATS);
623 for (i=0;i<nprocs;i++) {
624 if (rmdir(id_data[i].dname) != 0) {
625 fprintf(stderr, "Failed to delete %s\n", id_data[i].dname);
630 for (i=0;i<nprocs;i++) {
631 free(id_data[i].dname);
632 free(id_data[i].fname);