blob: d5bb46b38910746eccdf713164e71ef668a81f68 [file] [log] [blame]
/* Copyright (c) 2014 The Regents of the University of California
* Barret Rhoden <brho@cs.berkeley.edu>
* See LICENSE for details.
*
* Basic pthread switcher, bypassing the 2LS. Use for benchmarking and
* 2LS-inspiration. */
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include "misc-compat.h"
pthread_t th1, th2;
int nr_switch_loops = 100;
pthread_barrier_t barrier;
bool should_exit = FALSE;
static void __pth_switch_cb(struct uthread *uthread, void *target)
{
/* by not returning, this bypasses vcore entry and event checks, though
* when we pop back out of the 2LS, we'll check notif pending. think
* about this if you put this into your 2LS. */
current_uthread = NULL;
run_uthread((struct uthread*)target);
assert(0);
}
static void pth_switch_to(struct pthread_tcb *target)
{
uthread_yield(TRUE, __pth_switch_cb, target);
}
void *switch_thread(void *arg)
{
pthread_t other_thr = *(pthread_t*)arg;
pthread_barrier_wait(&barrier);
for (int i = 0; i < nr_switch_loops; i++) {
cmb();
if (should_exit)
return 0;
pth_switch_to(other_thr);
}
if (pthread_self() == th1) {
/* we need to break out of the switching cycle. when th2 runs
* again, it'll know to stop. but th1 needs to both exit and
* switch to th2. we do this by making th2 runnable by the pth
* schedop, then exiting */
should_exit = TRUE;
/* we also need to do this to th2 before it tries to exit, o/w
* we'll PF in __pthread_generic_yield. */
sched_ops->thread_runnable((struct uthread*)th2);
}
return 0;
}
int main(int argc, char** argv)
{
struct timeval start_tv = {0};
struct timeval end_tv = {0};
long usec_diff;
long nr_ctx_switches;
void *join_ret;
if (argc > 1)
nr_switch_loops = strtol(argv[1], 0, 10);
printf("Making 2 threads of %d switches each\n", nr_switch_loops);
parlib_never_yield = TRUE;
parlib_never_vc_request = TRUE;
pthread_need_tls(FALSE);
pthread_mcp_init(); /* gives us one vcore */
pthread_barrier_init(&barrier, NULL, 2);
/* each is passed the other's pthread_t. th1 starts the switching. */
if (pthread_create(&th1, NULL, &switch_thread, &th2))
perror("pth_create 1 failed");
/* thread 2 is created, but not put on the runnable list */
if (__pthread_create(&th2, NULL, &switch_thread, &th1))
perror("pth_create 2 failed");
if (gettimeofday(&start_tv, 0))
perror("Start time error...");
pthread_join(th1, &join_ret);
pthread_join(th2, &join_ret);
if (gettimeofday(&end_tv, 0))
perror("End time error...");
nr_ctx_switches = 2 * nr_switch_loops;
usec_diff = (end_tv.tv_sec - start_tv.tv_sec) * 1000000 +
(end_tv.tv_usec - start_tv.tv_usec);
printf("Done: %d loops\n", nr_switch_loops);
printf("Nr context switches: %ld\n", nr_ctx_switches);
printf("Time to run: %ld usec\n", usec_diff);
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));
}