Treat tabs as having eight spaces instead of four
[akaros.git] / tests / microb_test.c
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
50 static uint32_t __get_pcoreid(void)
51 {
52         return __procinfo.vcoremap[vcore_id()].pcoreid;
53 }
54
55 /* Testing functions here */
56
57 void 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
77 void 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
128 static 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
146 void *worker_thread(void* arg)
147 {       
148         microb_test();
149         return 0;
150 }
151
152 int 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