added keepalive tool
[tridge/junkcode.git] / thread_perf.c
1 /*
2   simple thread/process benchmark
3
4   Copyright (C) Andrew Tridgell <tridge@samba.org> 2003
5
6   Released under the GNU GPL version 2 or later
7 */
8
9 /*
10   this program is designed to test the relative performance of threads/processes
11   for operations typically performed by fileserving applications.
12 */
13
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <time.h>
18 #include <sys/time.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <signal.h>
25 #include <string.h>
26 #include <dirent.h>
27
28 #ifndef NO_THREADS
29 #include <pthread.h>
30 #endif
31
32 /* this contains per-task data */
33 static struct {
34         char *dname;
35         char *fname;
36 } *id_data;
37
38 /* these pipes are used for synchronised startup of the tasks */
39 static struct barrier {
40         int fd1[2];
41         int fd2[2];
42 } barriers[2];
43
44 /* setup a barrier */
45 static void barrier_setup(struct barrier *b)
46 {
47         if (pipe(b->fd1) != 0 || pipe(b->fd2) != 0) {
48                 fprintf(stderr,"Barrier setup failed\n");
49                 exit(1);
50         }
51 }
52
53 /* cleanup the barrier pipes */
54 static void barrier_cleanup(struct barrier *b)
55 {
56         close(b->fd1[0]);
57         close(b->fd1[1]);
58         close(b->fd2[0]);
59         close(b->fd2[1]);
60 }
61
62 /* wait for the parent to signal startup */
63 static void barrier_wait(struct barrier *b)
64 {
65         char c = 0;
66
67         if (write(b->fd1[1], &c, 1) != 1 ||
68             read(b->fd2[0], &c, 1) != 1) {
69                 fprintf(stderr, "Barrier wait failed\n");
70                 exit(1);
71         }
72 }
73
74 /* synchronise children. Return the amount of time since the last
75    barrier */
76 static double barrier_parent(struct barrier *b, int nprocs)
77 {
78         char *s = calloc(nprocs, 1);
79         int i, nwritten=0;
80         char c = 0;
81         double t;
82         static struct timeval tp1;
83         struct timeval tp2;
84
85         for (i=0;i<nprocs;i++) {
86                 while (read(b->fd1[0], &c, 1) != 1) ;
87         }
88
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);
95
96         while (nwritten != nprocs) {
97                 int n = write(b->fd2[1], s, nprocs-nwritten);
98                 if (n <= 0) {
99                         fprintf(stderr, "Barrier parent failed\n");
100                         exit(1);
101                 }
102                 nwritten += n;
103         }
104         free(s);
105         return t;
106 }
107
108 #ifndef NO_THREADS
109 /*
110   create a thread with initial function fn(private)
111 */
112 static pthread_t thread_start(void *(*fn)(int), int id)
113 {
114         pthread_t thread_id;
115         pthread_attr_t thread_attr;
116         int rc;
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);
122
123         if (rc != 0) {
124                 fprintf(stderr,"Thread create failed for id %d\n", id);
125                 exit(1);
126         }
127
128         return thread_id;
129 }
130
131 /* wait for a thread to exit */
132 static int thread_join(pthread_t id)
133 {
134         return pthread_join(id, NULL);
135 }
136 #endif
137
138
139 /*
140   create a process with initial function fn(private)
141 */
142 static pid_t proc_start(void *(*fn)(int), int id)
143 {
144         pid_t pid;
145
146         pid = fork();
147         if (pid == (pid_t)-1) {
148                 fprintf(stderr,"Fork failed for id %d\n", id);
149                 return pid;
150         }
151         if (pid == 0) {
152                 fn(id);
153                 exit(0);
154         }
155         return pid;
156 }
157
158 /* wait for a process to exit */
159 static int proc_join(pid_t id)
160 {
161         if (waitpid(id, NULL, 0) != id) {
162                 return -1;
163         }
164         return 0;
165 }
166
167 #ifndef NO_THREADS
168 /* run a function under a set of threads */
169 static double run_threads(int nthreads, void *(*fn)(int ))
170 {
171         int i;
172         pthread_t *ids = calloc(sizeof(*ids), nthreads);
173         double t;
174
175         barrier_setup(&barriers[0]);
176         barrier_setup(&barriers[1]);
177
178         for (i=0;i<nthreads;i++) {
179                 ids[i] = thread_start(fn, i);
180         }
181
182         barrier_parent(&barriers[0], nthreads);
183         t = barrier_parent(&barriers[1], nthreads);
184
185         for (i=0;i<nthreads;i++) {
186                 int rc;
187                 rc = thread_join(ids[i]);
188                 if (rc != 0) {
189                         fprintf(stderr, "Thread %d failed : %s\n", i, strerror(errno));
190                         exit(1);
191                 }
192         }
193
194         barrier_cleanup(&barriers[0]);
195         barrier_cleanup(&barriers[1]);
196
197         free(ids);
198
199         return t;
200 }
201 #endif
202
203 /* run a function under a set of processes */
204 static double run_processes(int nprocs, void *(*fn)(int ))
205 {
206         int i;
207         pid_t *ids = calloc(sizeof(*ids), nprocs);
208         double t;
209
210         barrier_setup(&barriers[0]);
211         barrier_setup(&barriers[1]);
212
213         for (i=0;i<nprocs;i++) {
214                 ids[i] = proc_start(fn, i);
215                 if (ids[i] == (pid_t)-1) {
216                         for (i--;i>=0;i--) {
217                                 kill(ids[i], SIGTERM);
218                         }
219                         exit(1);
220                 }
221         }
222
223         barrier_parent(&barriers[0], nprocs);
224         t = barrier_parent(&barriers[1], nprocs);
225
226         for (i=0;i<nprocs;i++) {
227                 int rc;
228                 rc = proc_join(ids[i]);
229                 if (rc != 0) {
230                         fprintf(stderr, "Process %d failed : %s\n", i, strerror(errno));
231                         exit(1);
232                 }
233         }
234
235         barrier_cleanup(&barriers[0]);
236         barrier_cleanup(&barriers[1]);
237
238         free(ids);
239
240         return t;
241 }
242
243
244
245 /***********************************************************************
246   a simple malloc speed test using a wide variety of malloc sizes
247 ************************************************************************/
248 static void *test_malloc(int id)
249 {
250 #define NMALLOCS 300
251         int i, j;
252         void *ptrs[NMALLOCS];
253
254         barrier_wait(&barriers[0]);
255
256         for (j=0;j<500;j++) {
257                 for (i=1;i<NMALLOCS;i++) {
258                         ptrs[i] = malloc(i);
259                         if (!ptrs[i]) {
260                                 printf("malloc(%d) failed!\n", i);
261                                 exit(1);
262                         }
263                 }
264                 for (i=1;i<NMALLOCS;i++) {
265                         free(ptrs[i]);
266                 }
267         }
268
269         barrier_wait(&barriers[1]);
270         return NULL;
271 }
272
273 /***********************************************************************
274   a simple setreuid speed test
275 ************************************************************************/
276 static void *test_setreuid(int id)
277 {
278         int i;
279         void *ptrs[NMALLOCS];
280
281         barrier_wait(&barriers[0]);
282         if (getuid() != 0) {
283                 if (id == 0) {
284                         printf("Skipping setreuid test for non-root user\n");
285                 }
286                 barrier_wait(&barriers[1]);
287                 return NULL;
288         }
289
290
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]);
296                         return NULL;
297                 }
298         }
299
300         barrier_wait(&barriers[1]);
301         return NULL;
302 }
303
304
305 /***********************************************************************
306  simple read/write testing using /dev/null and /dev/zero
307 ************************************************************************/
308 static void *test_readwrite(int id)
309 {
310         int i;
311         int fd_in, fd_out;
312         /* we use less than 1 page to prevent page table games */
313         char buf[4095];
314
315         barrier_wait(&barriers[0]);
316
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");
321                 exit(1);
322         }
323
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);
328                         exit(1);
329                 }
330         }
331
332         close(fd_in);
333         close(fd_out);
334         
335         barrier_wait(&barriers[1]);
336
337         return NULL;
338 }
339
340
341 /***********************************************************************
342 test stat() operations
343 ************************************************************************/
344 static void *test_stat(int id)
345 {
346         int i;
347
348         barrier_wait(&barriers[0]);
349
350         for (i=0;i<30000;i++) {
351                 struct stat st;
352                 if (stat(id_data[id].dname, &st) != 0) goto failed;
353                 if (stat(id_data[id].fname, &st) == 0) goto failed;
354         }
355
356         barrier_wait(&barriers[1]);
357
358         return NULL;
359
360 failed:
361         fprintf(stderr,"stat failed\n");
362         exit(1);
363 }
364
365 /***********************************************************************
366 test fstat() operations
367 ************************************************************************/
368 static void *test_fstat(int id)
369 {
370         int i, fd;
371
372         barrier_wait(&barriers[0]);
373
374         fd = open(id_data[id].fname, O_RDWR|O_CREAT, 0600);
375         if (fd == -1) goto failed;
376
377         for (i=0;i<1000000;i++) {
378                 struct stat st;
379                 if (fstat(fd, &st) != 0) goto failed;
380         }
381
382         close(fd);
383         unlink(id_data[id].fname);
384
385         barrier_wait(&barriers[1]);
386
387         return NULL;
388
389 failed:
390         fprintf(stderr,"fstat failed\n");
391         exit(1);
392 }
393
394 /***********************************************************************
395 test directory operations
396 ************************************************************************/
397 static void *test_dir(int id)
398 {
399         int i;
400
401         barrier_wait(&barriers[0]);
402
403         for (i=0;i<2000;i++) {
404                 DIR *d = opendir(id_data[id].dname);
405                 if (!d) goto failed;
406                 while (readdir(d)) {} ;
407                 closedir(d);
408         }
409
410         barrier_wait(&barriers[1]);
411         return NULL;
412
413 failed: 
414         fprintf(stderr,"dir failed\n");
415         exit(1);
416 }
417
418 /***********************************************************************
419 test directory operations
420 ************************************************************************/
421 static void *test_dirsingle(int id)
422 {
423         int i;
424
425         barrier_wait(&barriers[0]);
426
427         for (i=0;i<2000;i++) {
428                 DIR *d = opendir(".");
429                 if (!d) goto failed;
430                 while (readdir(d)) {} ;
431                 closedir(d);
432         }
433
434         barrier_wait(&barriers[1]);
435         return NULL;
436
437 failed: 
438         fprintf(stderr,"dirsingle failed\n");
439         exit(1);
440 }
441
442
443 /***********************************************************************
444 test create/unlink operations
445 ************************************************************************/
446 static void *test_create(int id)
447 {
448         int i;
449
450         barrier_wait(&barriers[0]);
451
452         for (i=0;i<3000;i++) {
453                 int fd;
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;
457                 close(fd);
458                 if (unlink(id_data[id].fname) != 0) goto failed;
459         }
460         
461         barrier_wait(&barriers[1]);
462         return NULL;
463
464 failed:
465         fprintf(stderr,"create failed\n");
466         exit(1);
467 }
468
469
470 /***********************************************************************
471 test fcntl lock operations
472 ************************************************************************/
473 static void *test_lock(int id)
474 {
475         int i;
476         int fd;
477
478         barrier_wait(&barriers[0]);
479
480         fd = open(id_data[id].fname, O_CREAT|O_RDWR, 0666);
481         if (fd == -1) goto failed;
482         unlink(id_data[id].fname);
483
484         for (i=0;i<20000;i++) {
485                 struct flock lock;
486                 lock.l_type = F_WRLCK;
487                 lock.l_whence = SEEK_SET;
488                 lock.l_start = (id*100) + (i%100);
489                 lock.l_len = 1;
490                 lock.l_pid = 0;
491         
492                 if (fcntl(fd,F_SETLK,&lock) != 0) goto failed;
493
494                 lock.l_type = F_UNLCK;
495
496                 if (fcntl(fd,F_SETLK,&lock) != 0) goto failed;
497         }
498
499         close(fd);
500         
501         barrier_wait(&barriers[1]);
502         return NULL;
503
504 failed:
505         fprintf(stderr,"lock failed\n");
506         exit(1);
507 }
508
509 /***********************************************************************
510 do nothing!
511 ************************************************************************/
512 static void *test_noop(int id)
513 {
514         barrier_wait(&barriers[0]);
515         barrier_wait(&barriers[1]);
516         return NULL;
517 }
518
519
520 /*
521   show the average and range of a set of results
522 */
523 static void show_result(const char *name, double *t, int nrepeats)
524 {
525         double mint, maxt, total;
526         int i;
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];
531                 total += t[i];
532         }
533         printf("%s  %5.2f +/- %.2f seconds\n", name, total/nrepeats, (maxt-mint)/2);
534 }
535
536
537 /* lock a byte range in a open file */
538 int main(int argc, char *argv[])
539 {
540         int nprocs, i;
541         char *tname = "ALL";
542 #define NREPEATS 10
543         struct {
544                 const char *name;
545                 void *(*fn)(int );
546         } tests[] = {
547                 {"noop", test_noop},
548                 {"malloc", test_malloc},
549                 {"setreuid", test_setreuid},
550                 {"readwrite", test_readwrite},
551                 {"stat", test_stat},
552                 {"fstat", test_fstat},
553                 {"dir", test_dir},
554                 {"dirsingle", test_dirsingle},
555                 {"create", test_create},
556                 {"lock", test_lock},
557                 {NULL, NULL}
558         };
559
560         if (argc <= 1) {
561                 printf("thread_perf NPROCS\n");
562                 exit(1);
563         }
564
565         nprocs = atoi(argv[1]);
566
567         if (argc > 2) {
568                 tname = argv[2];
569         }
570
571         id_data = calloc(nprocs, sizeof(*id_data));
572         if (!id_data) {
573                 exit(1);
574         }
575
576 #ifndef NO_THREADS
577         printf("NOTE! for accurate process results please compile with -DNO_THREADS and don't link to -lpthread\n\n");
578 #endif
579
580         for (i=0;i<nprocs;i++) {
581                 char s[30];
582                 sprintf(s, "testd_%d", i);
583                 id_data[i].dname = strdup(s);
584
585                 sprintf(s, "%s/test.dat", id_data[i].dname);
586                 id_data[i].fname = strdup(s);
587
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);
591                         exit(1);
592                 }
593
594                 unlink(id_data[i].fname);
595         }
596
597         for (i=0;tests[i].name;i++) {
598                 double t_threads[NREPEATS];
599                 double t_processes[NREPEATS];
600                 int j;
601
602                 if (strcasecmp(tname, "ALL") && strcasecmp(tests[i].name, tname)) {
603                         continue;
604                 }
605
606                 printf("Running test '%s' with %d tasks\n", tests[i].name, nprocs);
607
608                 for (j=0;j<NREPEATS;j++) {
609 #ifndef NO_THREADS
610                         t_threads[j]   = run_threads(nprocs, tests[i].fn);
611 #endif
612                         t_processes[j] = run_processes(nprocs, tests[i].fn);
613                 }
614 #ifndef NO_THREADS
615                 show_result("Threads  ", t_threads, NREPEATS);
616 #endif
617                 show_result("Processes", t_processes, NREPEATS);
618
619                 printf("\n");
620                 fflush(stdout);
621         }
622
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);
626                         exit(1);
627                 }
628         }
629
630         for (i=0;i<nprocs;i++) {
631                 free(id_data[i].dname);
632                 free(id_data[i].fname);
633         }
634         free(id_data);
635
636         return 0;
637 }