akaros/tests/pthread_switch.c
<<
>>
Prefs
   1/* Copyright (c) 2014 The Regents of the University of California
   2 * Barret Rhoden <brho@cs.berkeley.edu>
   3 * See LICENSE for details.
   4 *
   5 * Basic pthread switcher, bypassing the 2LS.  Use for benchmarking and
   6 * 2LS-inspiration. */
   7
   8#include <stdio.h>
   9#include <pthread.h>
  10#include <stdlib.h>
  11#include <unistd.h>
  12#include <sys/time.h>
  13#include "misc-compat.h"
  14
  15pthread_t th1, th2;
  16int nr_switch_loops = 100;
  17pthread_barrier_t barrier;
  18bool should_exit = FALSE;
  19
  20static void __pth_switch_cb(struct uthread *uthread, void *target)
  21{
  22        /* by not returning, this bypasses vcore entry and event checks, though
  23         * when we pop back out of the 2LS, we'll check notif pending.  think
  24         * about this if you put this into your 2LS. */
  25        current_uthread = NULL;
  26        run_uthread((struct uthread*)target);
  27        assert(0);
  28}
  29
  30static void pth_switch_to(struct pthread_tcb *target)
  31{
  32        uthread_yield(TRUE, __pth_switch_cb, target);
  33}
  34
  35void *switch_thread(void *arg)
  36{       
  37        pthread_t other_thr = *(pthread_t*)arg;
  38
  39        pthread_barrier_wait(&barrier);
  40        for (int i = 0; i < nr_switch_loops; i++) {
  41                cmb();
  42                if (should_exit)
  43                        return 0;
  44                pth_switch_to(other_thr);
  45        }
  46        if (pthread_self() == th1) {
  47                /* we need to break out of the switching cycle.  when th2 runs
  48                 * again, it'll know to stop.  but th1 needs to both exit and
  49                 * switch to th2.  we do this by making th2 runnable by the pth
  50                 * schedop, then exiting */
  51                should_exit = TRUE;
  52                /* we also need to do this to th2 before it tries to exit, o/w
  53                 * we'll PF in __pthread_generic_yield. */
  54                sched_ops->thread_runnable((struct uthread*)th2);
  55        }
  56        return 0;
  57}
  58
  59int main(int argc, char** argv) 
  60{
  61        struct timeval start_tv = {0};
  62        struct timeval end_tv = {0};
  63        long usec_diff;
  64        long nr_ctx_switches;
  65        void *join_ret;
  66
  67        if (argc > 1)
  68                nr_switch_loops = strtol(argv[1], 0, 10);
  69        printf("Making 2 threads of %d switches each\n", nr_switch_loops);
  70
  71        parlib_never_yield = TRUE;
  72        parlib_never_vc_request = TRUE;
  73        pthread_need_tls(FALSE);
  74        pthread_mcp_init();             /* gives us one vcore */
  75
  76        pthread_barrier_init(&barrier, NULL, 2);
  77        /* each is passed the other's pthread_t.  th1 starts the switching. */
  78        if (pthread_create(&th1, NULL, &switch_thread, &th2))
  79                perror("pth_create 1 failed");
  80        /* thread 2 is created, but not put on the runnable list */
  81        if (__pthread_create(&th2, NULL, &switch_thread, &th1))
  82                perror("pth_create 2 failed");
  83
  84        if (gettimeofday(&start_tv, 0))
  85                perror("Start time error...");
  86
  87        pthread_join(th1, &join_ret);
  88        pthread_join(th2, &join_ret);
  89
  90        if (gettimeofday(&end_tv, 0))
  91                perror("End time error...");
  92        nr_ctx_switches = 2 * nr_switch_loops;
  93        usec_diff = (end_tv.tv_sec - start_tv.tv_sec) * 1000000 +
  94                    (end_tv.tv_usec - start_tv.tv_usec);
  95        printf("Done: %d loops\n", nr_switch_loops);
  96        printf("Nr context switches: %ld\n", nr_ctx_switches);
  97        printf("Time to run: %ld usec\n", usec_diff);
  98        printf("Context switch latency: %d nsec\n",
  99               (int)(1000LL*usec_diff / nr_ctx_switches));
 100        printf("Context switches / sec: %d\n\n",
 101               (int)(1000000LL*nr_ctx_switches / usec_diff));
 102} 
 103