akaros/tests/microb_test.c
<<
>>
Prefs
   1/* Copyright (c) 2013 The Regents of the University of California
   2 * Barret Rhoden <brho@cs.berkeley.edu>
   3 * See LICENSE for details.
   4 *
   5 * Basic perf test for small functions.  Will run them in a loop and give you
   6 * the average cost per iteration.  It'll run them both as an SCP and an MCP.
   7 *
   8 * To use this, define a function of the form:
   9 *
  10 *      void my_test(unsigned long nr_loops)
  11 *
  12 * Which does some computation you wish to measure inside a loop that run
  13 * nr_loops times.  Then in microb_test(), add your function in a line such as:
  14 *
  15 *      test_time_ns(my_test, 100000);
  16 *
  17 * This macro will run your test and print the results.  Pick a loop amount that
  18 * is reasonable for your operation.  You can also use test_time_us() for longer
  19 * operations.
  20 *
  21 * Notes:
  22 * - I went with this style so you could do some prep work before and after the
  23 *   loop (instead of having a macro build the loop).  It's what I needed.
  24 * - Be sure to double check the ASM inside the loop to make sure the compiler
  25 *   isn't optimizing out your main work. 
  26 * - Make sure your function isn't static.  If it is static (and even if it is
  27 *   __attribute__((noinline))), if the function is called only once, the
  28 *   compiler will compile it differently (specifically, it will hardcode the
  29 *   number of loops into the function, instead of taking a parameter).
  30 *   Suddenly, the existence of a second test of the same function could change
  31 *   the performance of *both* test runs.  Incidentally, when this happened to
  32 *   me, the tests were *better* when this optimization didn't happen.  The way
  33 *   to avoid the optimization completely is to have extern functions, since the
  34 *   compiler can't assume it is only called once.  Though technically they
  35 *   still could do some optimizations, and the only really safe way is to put
  36 *   the tests in another .c file. */
  37
  38#include <stdio.h>
  39#include <pthread.h>
  40#include <stdlib.h>
  41#include <unistd.h>
  42#include <sys/time.h>
  43
  44/* OS dependent #incs */
  45#include <parlib/parlib.h>
  46#include <parlib/vcore.h>
  47#include <parlib/timing.h>
  48#include <parlib/stdio.h>
  49
  50static uint32_t __get_pcoreid(void)
  51{
  52        return __procinfo.vcoremap[vcore_id()].pcoreid;
  53}
  54
  55/* Testing functions here */
  56
  57void set_tlsdesc_test(unsigned long nr_loops)
  58{
  59#ifdef __i386__
  60        uint32_t vcoreid = vcore_id();
  61        void *mytls = get_tls_desc();
  62        void *vctls = get_vcpd_tls_desc(vcoreid);
  63        segdesc_t tmp = SEG(STA_W, (uint32_t)vctls, 0xffffffff, 3);
  64        uint32_t gs = (vcoreid << 3) | 0x07;
  65
  66        for (int i = 0; i < nr_loops; i++) {
  67                __procdata.ldt[vcoreid] = tmp;
  68                cmb();
  69                asm volatile("movl %0,%%gs" : : "r" (gs) : "memory");
  70        }
  71        set_tls_desc(mytls);
  72#endif
  73}
  74
  75/* Internal test infrastructure */
  76
  77void loop_overhead(unsigned long nr_loops)
  78{
  79        for (int i = 0; i < nr_loops; i++) {
  80                cmb();
  81        }
  82}
  83
  84/* Runs func(loops) and returns the usec elapsed */
  85#define __test_time_us(func, loops)                                            \
  86({                                                                             \
  87        struct timeval start_tv = {0};                                         \
  88        struct timeval end_tv = {0};                                           \
  89                                                                               \
  90        if (gettimeofday(&start_tv, 0))                                        \
  91                perror("Start time error...");                                 \
  92        (func)((loops));                                                       \
  93        if (gettimeofday(&end_tv, 0))                                          \
  94                perror("End time error...");                                   \
  95        ((end_tv.tv_sec - start_tv.tv_sec) * 1000000 +                         \
  96         (end_tv.tv_usec - start_tv.tv_usec));                                 \
  97})
  98
  99/* Runs func(loops) and returns the nsec elapsed */
 100#define __test_time_ns(func, loops)                                            \
 101({                                                                             \
 102        (__test_time_us((func), (loops)) * 1000);                              \
 103})
 104
 105/* Runs func(loops), subtracts the loop overhead, and prints the result */
 106#define test_time_us(func, loops)                                              \
 107({                                                                             \
 108        unsigned long long usec_diff;                                          \
 109                                                                               \
 110        usec_diff = __test_time_us((func),                                     \
 111                                   (loops)) - nsec_per_loop * loops / 1000;    \
 112        printf("\"%s\" total: %lluus, per iteration: %lluus\n", #func,         \
 113               usec_diff,                                                      \
 114               usec_diff / (loops));                                           \
 115})
 116
 117/* Runs func(loops), subtracts the loop overhead, and prints the result */
 118#define test_time_ns(func, loops)                                              \
 119({                                                                             \
 120        unsigned long long nsec_diff;                                          \
 121                                                                               \
 122        nsec_diff = __test_time_ns((func), (loops)) - nsec_per_loop * (loops); \
 123        printf("\"%s\" total: %lluns, per iteration: %lluns\n", #func,         \
 124               nsec_diff,                                                      \
 125               nsec_diff / (loops));                                           \
 126})
 127
 128static void microb_test(void)
 129{
 130        unsigned long long nsec_per_loop;
 131
 132        printf("We are %sin MCP mode, running on vcore %d, pcore %d\n",
 133               (in_multi_mode() ? "" : "not "), vcore_id(),
 134               __get_pcoreid());
 135        /* We divide the overhead by loops, and later we multiply again, which
 136         * drops off some accuracy at the expense of usability (can do different
 137         * iterations for different tests without worrying about recomputing the
 138         * loop overhead). */
 139        nsec_per_loop = __test_time_ns(loop_overhead, 100000) / 100000;
 140        printd("Loop overhead per loop: %lluns\n", nsec_per_loop);
 141
 142        /* Add your tests here.  Func name, number of loops */
 143        test_time_ns(set_tlsdesc_test , 100000);
 144}
 145
 146void *worker_thread(void* arg)
 147{       
 148        microb_test();
 149        return 0;
 150}
 151
 152int main(int argc, char** argv) 
 153{
 154        pthread_t child;
 155        void *child_ret;
 156
 157        microb_test();
 158        printf("Spawning worker thread, etc...\n");
 159        pthread_create(&child, NULL, &worker_thread, NULL);
 160        pthread_join(child, &child_ret);
 161} 
 162