pthread_test() and supporting changes
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 19 Nov 2012 23:56:20 +0000 (15:56 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 20 Nov 2012 00:06:32 +0000 (16:06 -0800)
The ugly bit is that the pthread 2LS wants to adjust the number of
vcores, but for testing, we want a fixed amount.  Hence the function to
toggle the bool.  We can sort out something better once we have a real
2LS.

tests/pthread_test.c
user/parlib/uthread.c
user/pthread/pthread.c
user/pthread/pthread.h

index 310594f..d5af34e 100644 (file)
@@ -1,9 +1,12 @@
 #include <stdio.h>
 #include <pthread.h>
 #include <stdlib.h>
-#include <parlib.h>
 #include <unistd.h>
+
+/* OS dependent #incs */
+#include <parlib.h>
 #include <vcore.h>
+#include <timing.h>
 
 pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
 #define printf_safe(...) {}
@@ -15,16 +18,26 @@ pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
 #define MAX_NR_TEST_THREADS 100000
 int nr_yield_threads = 100;
 int nr_yield_loops = 100;
+int nr_vcores = 0;
+int amt_fake_work = 0;
 
 pthread_t my_threads[MAX_NR_TEST_THREADS];
 void *my_retvals[MAX_NR_TEST_THREADS];
 
-__thread int my_id;
+bool ready = FALSE;
+
 void *yield_thread(void* arg)
 {      
+       /* Wait til all threads are created */
+       while (!ready)
+               cpu_relax();
        for (int i = 0; i < nr_yield_loops; i++) {
                printf_safe("[A] pthread %d %p on vcore %d, itr: %d\n", pthread_self()->id,
                       pthread_self(), vcore_id(), i);
+               /* Fakes some work by spinning a bit.  Amount varies per uth/vcore,
+                * scaled by fake_work */
+               if (amt_fake_work)
+                       udelay(amt_fake_work * (pthread_self()->id * (vcore_id() + 1)));
                pthread_yield();
                printf_safe("[A] pthread %p returned from yield on vcore %d, itr: %d\n",
                            pthread_self(), vcore_id(), i);
@@ -32,30 +45,59 @@ void *yield_thread(void* arg)
        return (void*)(pthread_self());
 }
 
-void *hello_thread(void* arg)
-{      
-       printf_safe("[A] pthread %p on vcore %d\n", pthread_self(), vcore_id());
-       return (void*)(pthread_self());
-}
-
 int main(int argc, char** argv) 
 {
+       struct timeval start_tv = {0};
+       struct timeval end_tv = {0};
+       long usec_diff;
+       long nr_ctx_switches;
+
        if (argc > 1)
                nr_yield_threads = strtol(argv[1], 0, 10);
        if (argc > 2)
                nr_yield_loops = strtol(argv[2], 0, 10);
+       if (argc > 3)
+               nr_vcores = strtol(argv[3], 0, 10);
+       if (argc > 4)
+               amt_fake_work = strtol(argv[4], 0, 10);
        nr_yield_threads = MIN(nr_yield_threads, MAX_NR_TEST_THREADS);
-       printf("Making %d threads of %d loops each\n", nr_yield_threads, nr_yield_loops);
+       printf("Making %d threads of %d loops each, on %d vcore(s), %d work\n",
+              nr_yield_threads, nr_yield_loops, nr_vcores, amt_fake_work);
+
+       /* OS dependent prep work */
+       if (nr_vcores) {
+               /* Only do the vcore trickery if requested */
+               pthread_can_vcore_request(FALSE);       /* 2LS won't manage vcores */
+               pthread_lib_init();                                     /* gives us one vcore */
+               vcore_request(nr_vcores - 1);           /* ghetto incremental interface */
+       }
+
        /* create and join on yield */
        for (int i = 0; i < nr_yield_threads; i++) {
                printf_safe("[A] About to create thread %d\n", i);
                assert(!pthread_create(&my_threads[i], NULL, &yield_thread, NULL));
        }
+       if (gettimeofday(&start_tv, 0))
+               perror("Start time error...");
+       ready = TRUE;                   /* signal to any spinning uthreads to start */
        for (int i = 0; i < nr_yield_threads; i++) {
                printf_safe("[A] About to join on thread %d(%p)\n", i, my_threads[i]);
                pthread_join(my_threads[i], &my_retvals[i]);
                printf_safe("[A] Successfully joined on thread %d (retval: %p)\n", i,
                            my_retvals[i]);
        }
-       printf("Exiting cleanly!\n");
+       if (gettimeofday(&end_tv, 0))
+               perror("End time error...");
+       nr_ctx_switches = nr_yield_threads * nr_yield_loops;
+       usec_diff = (end_tv.tv_sec - start_tv.tv_sec) * 1000000 +
+                   (end_tv.tv_usec - start_tv.tv_usec);
+       printf("Done: %d uthreads, %d loops, %d vcores, %d work\n",
+              nr_yield_threads, nr_yield_loops, nr_vcores, amt_fake_work);
+       printf("Nr context switches: %d\n", nr_ctx_switches);
+       printf("Time to run: %d usec\n", usec_diff);
+       if (nr_vcores == 1)
+               printf("Context switch latency: %f usec\n",
+                      (float)usec_diff / (float)nr_ctx_switches);
+       printf("Context switches / sec: %f\n\n",
+              ((float)nr_ctx_switches / (float)usec_diff) * 1000000);
 } 
index 6fecbf1..f702c53 100644 (file)
@@ -62,10 +62,10 @@ static void uthread_manage_thread0(struct uthread *uthread)
  * returns, you're in _M mode, still running thread0, on vcore0 */
 int uthread_lib_init(struct uthread *uthread)
 {
-       /* Make sure this only runs once */
+       /* Make sure this only initializes once */
        static bool initialized = FALSE;
        if (initialized)
-               return -1;
+               return 0;
        initialized = TRUE;
        /* Init the vcore system */
        assert(!vcore_init());
index 062df86..b21524e 100644 (file)
@@ -23,6 +23,7 @@ struct mcs_pdr_lock queue_lock;
 pthread_once_t init_once = PTHREAD_ONCE_INIT;
 int threads_ready = 0;
 int threads_active = 0;
+bool can_adjust_vcores = TRUE;
 
 /* Array of per-vcore structs to manage waiting on syscalls and handling
  * overflow.  Init'd in pth_init(). */
@@ -100,7 +101,8 @@ void __attribute__((noreturn)) pth_sched_entry(void)
                printd("[P] No threads, vcore %d is yielding\n", vcore_id());
                /* TODO: you can imagine having something smarter here, like spin for a
                 * bit before yielding (or not at all if you want to be greedy). */
-               vcore_yield(FALSE);
+               if (can_adjust_vcores)
+                       vcore_yield(FALSE);
        } while (1);
        assert(new_thread->state == PTH_RUNNABLE);
        run_uthread((struct uthread*)new_thread);
@@ -143,7 +145,8 @@ void pth_thread_runnable(struct uthread *uthread)
        mcs_pdr_unlock(&queue_lock);
        /* Smarter schedulers should look at the num_vcores() and how much work is
         * going on to make a decision about how many vcores to request. */
-       vcore_request(threads_ready);
+       if (can_adjust_vcores)
+               vcore_request(threads_ready);
 }
 
 /* For some reason not under its control, the uthread stopped running (compared
@@ -258,6 +261,16 @@ void pth_spawn_thread(uintptr_t pc_start, void *data)
 {
 }
 
+/* Akaros pthread extensions / hacks */
+
+/* Tells the pthread 2LS to not change the number of vcores.  This means it will
+ * neither request vcores nor yield vcores.  Only used for testing. */
+void pthread_can_vcore_request(bool can)
+{
+       /* checked when we would request or yield */
+       can_adjust_vcores = can;
+}
+
 /* Pthread interface stuff and helpers */
 
 int pthread_attr_init(pthread_attr_t *a)
@@ -309,12 +322,12 @@ int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize)
 
 /* Do whatever init you want.  At some point call uthread_lib_init() and pass it
  * a uthread representing thread0 (int main()) */
-static int pthread_lib_init(void)
+int pthread_lib_init(void)
 {
-       /* Make sure this only runs once */
+       /* Make sure this only initializes once */
        static bool initialized = FALSE;
        if (initialized)
-               return -1;
+               return 0;
        initialized = TRUE;
        uintptr_t mmap_block;
        mcs_pdr_init(&queue_lock);
index 9ee1368..4d4dd79 100644 (file)
@@ -122,6 +122,10 @@ typedef int pthread_barrierattr_t;
 typedef int pthread_once_t;
 typedef void** pthread_key_t;
 
+/* Akaros pthread extensions / hacks */
+void pthread_can_vcore_request(bool can);      /* default is TRUE */
+int pthread_lib_init(void);
+
 /* The pthreads API */
 int pthread_attr_init(pthread_attr_t *);
 int pthread_attr_destroy(pthread_attr_t *);