-#include <rstdio.h>
+/* Copyright (c) 2010-14 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Basic test for pthreading. Spawns a bunch of threads that yield.
+ *
+ * To build on linux, cd into tests and run:
+ * $ gcc -O2 -std=gnu99 -fno-stack-protector -g pthread_test.c -lpthread
+ *
+ * Make sure you run it with taskset to fix the number of vcores/cpus. */
+
+#define _GNU_SOURCE /* for pth_yield on linux */
+
+#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
-#include <parlib.h>
#include <unistd.h>
+#include <sys/time.h>
+#include "misc-compat.h" /* OS dependent #incs */
+
+/* These are here just to have the compiler test the _INITIALIZERS */
+pthread_cond_t dummy_cond = PTHREAD_COND_INITIALIZER;
+pthread_mutex_t dummy_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-#define printf_safe(...) \
+#define printf_safe(...) {}
+//#define printf_safe(...) \
pthread_mutex_lock(&lock); \
printf(__VA_ARGS__); \
pthread_mutex_unlock(&lock);
-pthread_t t1;
-pthread_t t2;
-pthread_t t3;
+#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;
-#define NUM_TEST_THREADS 10
+pthread_t my_threads[MAX_NR_TEST_THREADS];
+void *my_retvals[MAX_NR_TEST_THREADS];
-pthread_t my_threads[NUM_TEST_THREADS];
-void *my_retvals[NUM_TEST_THREADS];
+pthread_barrier_t barrier;
-__thread int my_id;
void *yield_thread(void* arg)
{
- for (int i = 0; i < 10; i++) {
- printf_safe("[A] pthread %d on vcore %d\n", pthread_self()->id, vcore_id());
+ /* Wait til all threads are created */
+ pthread_barrier_wait(&barrier);
+ for (int i = 0; i < nr_yield_loops; i++) {
+ printf_safe("[A] pthread %d %p on vcore %d, itr: %d\n",
+ pthread_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_id() * (vcore_id() +
+ 2)));
pthread_yield();
- printf_safe("[A] pthread %d returned from yield on vcore %d\n",
- pthread_self()->id, vcore_id());
+ printf_safe("[A] pthread %p returned from yield on vcore %d, itr: %d\n",
+ pthread_self(), vcore_id(), i);
}
- return (void*)(pthread_self()->id);
-}
-
-void *hello_thread(void* arg)
-{
- printf_safe("[A] pthread %d on vcore %d\n", pthread_self()->id, vcore_id());
- return (void*)(pthread_self()->id);
+ return (void*)(pthread_self());
}
int main(int argc, char** argv)
{
- void *retval1 = 0;
- void *retval2 = 0;
- void *retval3 = 0;
+ struct timeval start_tv = {0};
+ struct timeval end_tv = {0};
+ long usec_diff;
+ long nr_ctx_switches;
- /* yield test */
- printf_safe("[A] About to create thread 1\n");
- pthread_create(&t1, NULL, &yield_thread, NULL);
- printf_safe("[A] About to create thread 2\n");
- pthread_create(&t2, NULL, &yield_thread, NULL);
- printf_safe("[A] About to create thread 3\n");
- pthread_create(&t3, NULL, &yield_thread, NULL);
- /* join on them */
- printf_safe("[A] About to join on thread 1\n");
- pthread_join(t1, &retval1);
- printf_safe("[A] Successfully joined on thread 1 (retval: %p)\n", retval1);
- printf_safe("[A] About to join on thread 2\n");
- pthread_join(t2, &retval2);
- printf_safe("[A] Successfully joined on thread 2 (retval: %p)\n", retval2);
- printf_safe("[A] About to join on thread 3\n");
- pthread_join(t3, NULL);
- printf_safe("[A] Successfully joined on thread 3 (retval: %p)\n", retval3);
+ 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, on %d vcore(s), %d work\n",
+ nr_yield_threads, nr_yield_loops, nr_vcores, amt_fake_work);
- /* create and join on hellos */
- for (int i = 1; i < NUM_TEST_THREADS; i++) {
- printf_safe("[A] About to create thread %d\n", i);
- pthread_create(&my_threads[i], NULL, &hello_thread, NULL);
+ /* OS dependent prep work */
+#ifdef __ros__
+ if (nr_vcores) {
+ /* Only do the vcore trickery if requested */
+ parlib_never_yield = TRUE;
+ pthread_need_tls(FALSE);
+ pthread_mcp_init(); /* gives us one vcore */
+ vcore_request_total(nr_vcores);
+ parlib_never_vc_request = TRUE;
+ for (int i = 0; i < nr_vcores; i++) {
+ printf_safe("Vcore %d mapped to pcore %d\n", i,
+ __procinfo.vcoremap[i].pcoreid);
}
- for (int i = 1; i < NUM_TEST_THREADS; i++) {
- printf_safe("[A] About to join on thread %d\n", i);
- pthread_join(my_threads[i], &my_retvals[i]);
- printf_safe("[A] Successfully joined on thread %d (retval: %p)\n", i,
- my_retvals[i]);
- }
- // Hack for now to make this process exit cleanly
- // For some reason, libc decided to call sys_yield() while we
- // are exiting, which breaks our model in multicore mode...
- sys_proc_destroy(getpid(),0);
+ }
+ struct uth_join_request *join_reqs;
+
+ join_reqs = malloc(nr_yield_threads * sizeof(struct uth_join_request));
+ for (int i = 0; i < nr_yield_threads; i++)
+ join_reqs[i].retval_loc = &my_retvals[i];
+ assert(join_reqs);
+#endif /* __ros__ */
+
+ pthread_barrier_init(&barrier, NULL, nr_yield_threads);
+ /* create and join on yield */
+ for (int i = 0; i < nr_yield_threads; i++) {
+ printf_safe("[A] About to create thread %d\n", i);
+ if (pthread_create(&my_threads[i], NULL, &yield_thread, NULL))
+ perror("pth_create failed");
+ }
+ if (gettimeofday(&start_tv, 0))
+ perror("Start time error...");
+ /* Akaros supports parallel join */
+#ifdef __ros__
+ for (int i = 0; i < nr_yield_threads; i++)
+ join_reqs[i].uth = (struct uthread*)my_threads[i];
+ uthread_join_arr(join_reqs, nr_yield_threads);
+#else
+ 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] Successful join on thread %d (retval: %p)\n",
+ i, my_retvals[i]);
+ }
+#endif
+ 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: %ld\n", nr_ctx_switches);
+ printf("Time to run: %ld usec\n", usec_diff);
+ if (nr_vcores == 1)
+ printf("Context switch latency: %d nsec\n",
+ (int)(1000LL*usec_diff / nr_ctx_switches));
+ printf("Context switches / sec: %d\n\n",
+ (int)(1000000LL*nr_ctx_switches / usec_diff));
}