nicer formatting
[tridge/junkcode.git] / thread_perf.c
index 3f70789f9a2a74bbfb261646f5d290a3d11917d4..27cb308b4ddcf7e7594d9732de2d336d9e333f37 100644 (file)
@@ -5,6 +5,12 @@
 
   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 <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <signal.h>
 #include <string.h>
 #include <dirent.h>
-#include <signal.h>
-#include <sys/ipc.h>
-#include <sys/shm.h>
+
+#ifndef NO_THREADS
 #include <pthread.h>
+#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]);
+}
 
-static void start_timer(void);
+/* wait for the parent to signal startup */
+static void barrier_wait(struct barrier *b)
+{
+       char c = 0;
 
-/* this shared memory area is used to synchronise the startup times of
-   the tasks */
-static volatile int *startup_ptr;
-static int wait_fd;
+       if (write(b->fd1[1], &c, 1) != 1 ||
+           read(b->fd2[0], &c, 1) != 1) {
+               fprintf(stderr, "Barrier wait failed\n");
+               exit(1);
+       }
+}
 
-/* wait for the startup pointer */
-static void startup_wait(int id)
+/* synchronise children. Return the amount of time since the last
+   barrier */
+static double barrier_parent(struct barrier *b, int nprocs)
 {
-       fd_set s;
-       startup_ptr[id] = 1;
-
-       /* we wait till the wait_fd is readable - this gives
-          a nice in-kernel barrier wait */
-       do {
-               FD_ZERO(&s);
-               FD_SET(wait_fd, &s);
-       } while (select(wait_fd+1, &s, NULL, NULL, NULL) != 1);
+       char *s = calloc(nprocs, 1);
+       int i, nwritten=0;
+       char c = 0;
+       double t;
+       static struct timeval tp1;
+       struct timeval tp2;
+
+       for (i=0;i<nprocs;i++) {
+               while (read(b->fd1[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)
 */
@@ -70,6 +133,7 @@ static int thread_join(pthread_t id)
 {
        return pthread_join(id, NULL);
 }
+#endif
 
 
 /*
@@ -100,36 +164,23 @@ static int proc_join(pid_t id)
        return 0;
 }
 
-
+#ifndef NO_THREADS
 /* run a function under a set of threads */
-static void run_threads(int nthreads, void *(*fn)(int ))
+static double run_threads(int nthreads, void *(*fn)(int ))
 {
        int i;
-       pthread_t *ids = malloc(sizeof(*ids) * nthreads);
-       int fd[2];
-       char c = 0;
-
-       if (pipe(fd) != 0) {
-               fprintf(stderr,"Pipe failed\n");
-               exit(1);
-       }
-       wait_fd = fd[0];
+       pthread_t *ids = calloc(sizeof(*ids), nthreads);
+       double t;
 
-       for (i=0;i<nthreads;i++) {
-               startup_ptr[i] = 0;
-       }
+       barrier_setup(&barriers[0]);
+       barrier_setup(&barriers[1]);
 
        for (i=0;i<nthreads;i++) {
                ids[i] = thread_start(fn, i);
        }
 
-       for (i=0;i<nthreads;i++) {
-               while (startup_ptr[i] != 1) usleep(1);
-       }
-
-       start_timer();
-
-       write(fd[1], &c, 1);
+       barrier_parent(&barriers[0], nthreads);
+       t = barrier_parent(&barriers[1], nthreads);
 
        for (i=0;i<nthreads;i++) {
                int rc;
@@ -140,27 +191,24 @@ static void run_threads(int nthreads, void *(*fn)(int ))
                }
        }
 
-       close(fd[0]);
-       close(fd[1]);
+       barrier_cleanup(&barriers[0]);
+       barrier_cleanup(&barriers[1]);
+
+       free(ids);
+
+       return t;
 }
+#endif
 
 /* run a function under a set of processes */
-static void run_processes(int nprocs, void *(*fn)(int ))
+static double run_processes(int nprocs, void *(*fn)(int ))
 {
        int i;
-       pid_t *ids = malloc(sizeof(*ids) * nprocs);
-       int fd[2];
-       char c = 0;
+       pid_t *ids = calloc(sizeof(*ids), nprocs);
+       double t;
 
-       if (pipe(fd) != 0) {
-               fprintf(stderr,"Pipe failed\n");
-               exit(1);
-       }
-       wait_fd = fd[0];
-
-       for (i=0;i<nprocs;i++) {
-               startup_ptr[i] = 0;
-       }
+       barrier_setup(&barriers[0]);
+       barrier_setup(&barriers[1]);
 
        for (i=0;i<nprocs;i++) {
                ids[i] = proc_start(fn, i);
@@ -172,13 +220,8 @@ static void run_processes(int nprocs, void *(*fn)(int ))
                }
        }
 
-       for (i=0;i<nprocs;i++) {
-               while (startup_ptr[i] != 1) usleep(1);
-       }
-
-       start_timer();
-
-       write(fd[1], &c, 1);
+       barrier_parent(&barriers[0], nprocs);
+       t = barrier_parent(&barriers[1], nprocs);
 
        for (i=0;i<nprocs;i++) {
                int rc;
@@ -189,8 +232,12 @@ static void run_processes(int nprocs, void *(*fn)(int ))
                }
        }
 
-       close(fd[0]);
-       close(fd[1]);
+       barrier_cleanup(&barriers[0]);
+       barrier_cleanup(&barriers[1]);
+
+       free(ids);
+
+       return t;
 }
 
 
@@ -204,7 +251,7 @@ static void *test_malloc(int id)
        int i, j;
        void *ptrs[NMALLOCS];
 
-       startup_wait(id);
+       barrier_wait(&barriers[0]);
 
        for (j=0;j<500;j++) {
                for (i=1;i<NMALLOCS;i++) {
@@ -218,6 +265,8 @@ static void *test_malloc(int id)
                        free(ptrs[i]);
                }
        }
+
+       barrier_wait(&barriers[1]);
        return NULL;
 }
 
@@ -232,7 +281,7 @@ static void *test_readwrite(int id)
        /* we use less than 1 page to prevent page table games */
        char buf[4095];
 
-       startup_wait(id);
+       barrier_wait(&barriers[0]);
 
        fd_in = open("/dev/zero", O_RDONLY);
        fd_out = open("/dev/null", O_WRONLY);
@@ -252,6 +301,8 @@ static void *test_readwrite(int id)
        close(fd_in);
        close(fd_out);
        
+       barrier_wait(&barriers[1]);
+
        return NULL;
 }
 
@@ -262,30 +313,50 @@ test stat() operations
 static void *test_stat(int id)
 {
        int i;
-       char fname[60];
-        char dname[30];
 
-       startup_wait(id);
+       barrier_wait(&barriers[0]);
 
-        sprintf(dname, "testd_%d", id);
-        rmdir(dname);
+       for (i=0;i<30000;i++) {
+               struct stat st;
+               if (stat(id_data[id].dname, &st) != 0) goto failed;
+               if (stat(id_data[id].fname, &st) == 0) goto failed;
+       }
 
-        if (mkdir(dname, 0777) != 0) goto failed;
+       barrier_wait(&barriers[1]);
 
-       sprintf(fname, "%s/test%d.dat", dname, id);
+       return NULL;
 
-       for (i=0;i<10000;i++) {
+failed:
+       fprintf(stderr,"stat failed\n");
+       exit(1);
+}
+
+/***********************************************************************
+test fstat() operations
+************************************************************************/
+static void *test_fstat(int id)
+{
+       int i, fd;
+
+       barrier_wait(&barriers[0]);
+
+       fd = open(id_data[id].fname, O_RDWR|O_CREAT, 0600);
+       if (fd == -1) goto failed;
+
+       for (i=0;i<1000000;i++) {
                struct stat st;
-               if (stat(dname, &st) != 0) goto failed;
-               if (stat(fname, &st) == 0) goto failed;
+               if (fstat(fd, &st) != 0) goto failed;
        }
 
-        if (rmdir(dname) != 0) goto failed;
-       
+       close(fd);
+       unlink(id_data[id].fname);
+
+       barrier_wait(&barriers[1]);
+
        return NULL;
 
 failed:
-       fprintf(stderr,"stat failed\n");
+       fprintf(stderr,"fstat failed\n");
        exit(1);
 }
 
@@ -295,24 +366,17 @@ test directory operations
 static void *test_dir(int id)
 {
         int i;
-        char dname[30];
-
-       startup_wait(id);
 
-        sprintf(dname, "testd_%d", id);
-        rmdir(dname);
-
-        if (mkdir(dname, 0777) != 0) goto failed;
+       barrier_wait(&barriers[0]);
 
         for (i=0;i<2000;i++) {
-                DIR *d = opendir(dname);
+                DIR *d = opendir(id_data[id].dname);
                 if (!d) goto failed;
                 while (readdir(d)) {} ;
                 closedir(d);
         }
 
-        if (rmdir(dname) != 0) goto failed;
-
+       barrier_wait(&barriers[1]);
         return NULL;
 
 failed: 
@@ -321,13 +385,13 @@ failed:
 }
 
 /***********************************************************************
-test directory operations on a single directory
+test directory operations
 ************************************************************************/
 static void *test_dirsingle(int id)
 {
         int i;
 
-       startup_wait(id);
+       barrier_wait(&barriers[0]);
 
         for (i=0;i<2000;i++) {
                 DIR *d = opendir(".");
@@ -336,42 +400,34 @@ static void *test_dirsingle(int id)
                 closedir(d);
         }
 
+       barrier_wait(&barriers[1]);
         return NULL;
 
 failed: 
-        fprintf(stderr,"dir failed\n");
+        fprintf(stderr,"dirsingle failed\n");
         exit(1);
 }
 
+
 /***********************************************************************
 test create/unlink operations
 ************************************************************************/
 static void *test_create(int id)
 {
        int i;
-       char fname[60];
-        char dname[30];
-
-       startup_wait(id);
-
-        sprintf(dname, "testd_%d", id);
-        rmdir(dname);
-
-        if (mkdir(dname, 0777) != 0) goto failed;
 
-       sprintf(fname, "%s/test%d.dat", dname, id);
+       barrier_wait(&barriers[0]);
 
-       for (i=0;i<1000;i++) {
+       for (i=0;i<3000;i++) {
                int fd;
-               fd = open(fname, O_CREAT|O_TRUNC|O_RDWR, 0666);
+               fd = open(id_data[id].fname, O_CREAT|O_TRUNC|O_RDWR, 0666);
                if (fd == -1) goto failed;
-               if (open(fname, O_CREAT|O_TRUNC|O_RDWR|O_EXCL, 0666) != -1) goto failed;
+               if (open(id_data[id].fname, O_CREAT|O_TRUNC|O_RDWR|O_EXCL, 0666) != -1) goto failed;
                close(fd);
-               if (unlink(fname) != 0) goto failed;
+               if (unlink(id_data[id].fname) != 0) goto failed;
        }
        
-        if (rmdir(dname) != 0) goto failed;
-
+       barrier_wait(&barriers[1]);
        return NULL;
 
 failed:
@@ -386,16 +442,13 @@ test fcntl lock operations
 static void *test_lock(int id)
 {
        int i;
-       char fname[30];
        int fd;
 
-       startup_wait(id);
-
-       sprintf(fname, "test%d.dat", id);
+       barrier_wait(&barriers[0]);
 
-       fd = open(fname, O_CREAT|O_RDWR, 0666);
+       fd = open(id_data[id].fname, O_CREAT|O_RDWR, 0666);
        if (fd == -1) goto failed;
-       unlink(fname);
+       unlink(id_data[id].fname);
 
        for (i=0;i<20000;i++) {
                struct flock lock;
@@ -414,6 +467,7 @@ static void *test_lock(int id)
 
        close(fd);
        
+       barrier_wait(&barriers[1]);
        return NULL;
 
 failed:
@@ -426,7 +480,8 @@ do nothing!
 ************************************************************************/
 static void *test_noop(int id)
 {
-       startup_wait(id);
+       barrier_wait(&barriers[0]);
+       barrier_wait(&barriers[1]);
        return NULL;
 }
 
@@ -444,44 +499,7 @@ static void show_result(const char *name, double *t, int nrepeats)
                if (t[i] > maxt) maxt = t[i];
                total += t[i];
        }
-       printf("%s  %.2f +/- %.2f seconds\n", name, total/nrepeats, (maxt-mint)/2);
-}
-
-
-static struct timeval tp1,tp2;
-
-static void start_timer(void)
-{
-       gettimeofday(&tp1,NULL);
-}
-
-static double end_timer(void)
-{
-       gettimeofday(&tp2,NULL);
-       return (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) - 
-               (tp1.tv_sec + (tp1.tv_usec*1.0e-6));
-}
-
-/* 
-   this is used to create the synchronised startup area 
-*/
-void *shm_setup(int size)
-{
-       int shmid;
-       void *ret;
-
-       shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
-       if (shmid == -1) {
-               fprintf(stderr, "can't get shared memory\n");
-               return NULL;
-       }
-       ret = (void *)shmat(shmid, 0, 0);
-       if (!ret || ret == (void *)-1) {
-               fprintf(stderr, "can't attach to shared memory\n");
-               return NULL;
-       }
-       shmctl(shmid, IPC_RMID, 0);
-       return ret;
+       printf("%s  %5.2f +/- %.2f seconds\n", name, total/nrepeats, (maxt-mint)/2);
 }
 
 
@@ -499,6 +517,7 @@ int main(int argc, char *argv[])
                {"malloc", test_malloc},
                {"readwrite", test_readwrite},
                {"stat", test_stat},
+               {"fstat", test_fstat},
                {"dir", test_dir},
                {"dirsingle", test_dirsingle},
                {"create", test_create},
@@ -517,11 +536,32 @@ int main(int argc, char *argv[])
                tname = argv[2];
        }
 
-       startup_ptr = shm_setup(sizeof(*startup_ptr) * nprocs);
-       if (!startup_ptr) {
+       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<nprocs;i++) {
+               char s[30];
+               sprintf(s, "testd_%d", i);
+               id_data[i].dname = strdup(s);
+
+               sprintf(s, "%s/test.dat", id_data[i].dname);
+               id_data[i].fname = strdup(s);
+
+               rmdir(id_data[i].dname);
+               if (mkdir(id_data[i].dname, 0777) != 0) {
+                       fprintf(stderr, "Failed to create %s\n", id_data[i].dname);
+                       exit(1);
+               }
+
+               unlink(id_data[i].fname);
+       }
+
        for (i=0;tests[i].name;i++) {
                double t_threads[NREPEATS];
                double t_processes[NREPEATS];
@@ -531,23 +571,35 @@ int main(int argc, char *argv[])
                        continue;
                }
 
-               printf("Running test '%s'\n", tests[i].name);
+               printf("Running test '%s' with %d tasks\n", tests[i].name, nprocs);
 
                for (j=0;j<NREPEATS;j++) {
-                       start_timer();
-                       run_threads(nprocs, tests[i].fn);
-                       t_threads[j] = end_timer();
-
-                       start_timer();
-                       run_processes(nprocs, tests[i].fn);
-                       t_processes[j] = end_timer();
+#ifndef NO_THREADS
+                       t_threads[j]   = run_threads(nprocs, tests[i].fn);
+#endif
+                       t_processes[j] = run_processes(nprocs, tests[i].fn);
                }
+#ifndef NO_THREADS
                show_result("Threads  ", t_threads, NREPEATS);
+#endif
                show_result("Processes", t_processes, NREPEATS);
 
                printf("\n");
                fflush(stdout);
        }
 
+       for (i=0;i<nprocs;i++) {
+               if (rmdir(id_data[i].dname) != 0) {
+                       fprintf(stderr, "Failed to delete %s\n", id_data[i].dname);
+                       exit(1);
+               }
+       }
+
+       for (i=0;i<nprocs;i++) {
+               free(id_data[i].dname);
+               free(id_data[i].fname);
+       }
+       free(id_data);
+
        return 0;
 }