1 /****************************************************************************** 2 * 3 * Copyright International Business Machines Corp., 2006, 2008 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 13 * the GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 * 19 * NAME 20 * periodic_cpu_load.c 21 * 22 * DESCRIPTION 23 * Measure variation in computational execution time 24 * at various periods and priorities. 25 * 26 * USAGE: 27 * Use run_auto.sh script in current directory to build and run test. 28 * 29 * AUTHOR 30 * Darren Hart <dvhltc (at) us.ibm.com> 31 * 32 * HISTORY 33 * 2007-April-27: Initial version by Darren Hart <dvhltc (at) us.ibm.com> 34 * 35 * This line has to be added to avoid a stupid CVS problem 36 *****************************************************************************/ 37 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <math.h> 41 #include <librttest.h> 42 #include <libstats.h> 43 44 #define PRIO_A 63 45 #define PRIO_B 53 46 #define PRIO_C 43 47 48 #define PERIOD_A 40*NS_PER_MS 49 #define PERIOD_B 80*NS_PER_MS 50 #define PERIOD_C 160*NS_PER_MS 51 52 #define CALC_LOOPS_A (27*100) 53 #define CALC_LOOPS_B (50*100) 54 #define CALC_LOOPS_C (72*100) 55 56 #define NUM_GROUPS 3 57 #define THREADS_PER_GROUP 4 58 59 //#define ITERATIONS 100 /* short functional test run */ 60 #define ITERATIONS 6000 /* about 15 minutes @ 2GHz on 1 CPU */ 61 //#define ITERATIONS 1000 /* min iters for 3 nines */ 62 // FIXME: need some kind of passing criteria calculation 63 //#define PASS_US 100 64 65 int fail[THREADS_PER_GROUP * NUM_GROUPS]; 66 stats_container_t dat[THREADS_PER_GROUP * NUM_GROUPS]; 67 stats_record_t rec; 68 stats_quantiles_t quantiles[THREADS_PER_GROUP * NUM_GROUPS]; 69 static const char groupname[NUM_GROUPS] = "ABC"; 70 71 static int iterations = ITERATIONS; 72 static int ret = 0; 73 74 void usage(void) 75 { 76 rt_help(); 77 printf("periodic_cpu_load specific options:\n"); 78 printf 79 (" -iITERATIONS number of iterations to calculate the average over\n"); 80 } 81 82 int parse_args(int c, char *v) 83 { 84 int handled = 1; 85 switch (c) { 86 break; 87 case 'i': 88 iterations = atoi(v); 89 break; 90 case 'h': 91 usage(); 92 exit(0); 93 default: 94 handled = 0; 95 break; 96 } 97 return handled; 98 } 99 100 struct periodic_arg { 101 int period; 102 int iterations; 103 void *(*func) (void *); 104 void *arg; 105 }; 106 107 void *calc(void *arg) 108 { 109 int i, j; 110 int loops = (intptr_t) arg; 111 for (i = 0; i < loops; i++) { 112 for (j = 0; j < 125; j++) { 113 // Sum of the numbers up to J 114 int temp = j * (j + 1) / 2; 115 (void)temp; 116 } 117 } 118 return NULL; 119 } 120 121 void *periodic_thread(void *thread) 122 { 123 struct thread *t = (struct thread *)thread; 124 struct periodic_arg *parg = (struct periodic_arg *)t->arg; 125 nsec_t period = parg->period; 126 void *(*func) (void *) = parg->func; 127 128 int i = 0; 129 nsec_t next, now; 130 nsec_t exe_start, exe_end, exe_time; 131 132 next = rt_gettime(); 133 while (i < parg->iterations) { 134 next += period; 135 if (rt_gettime() > next) { 136 printf("TID %d missed period, aborting\n", t->id); 137 fail[t->id] = 1; 138 break; 139 } 140 exe_start = rt_gettime(); 141 func(parg->arg); 142 exe_end = rt_gettime(); 143 exe_time = exe_end - exe_start; 144 rec.x = i; 145 rec.y = exe_time / NS_PER_US; 146 stats_container_append(&dat[t->id], rec); 147 148 i++; 149 150 now = rt_gettime(); 151 if (now > next) { 152 printf 153 ("Missed period, aborting (calc took too long)\n"); 154 fail[t->id] = 1; 155 break; 156 } 157 rt_nanosleep(next - now); 158 } 159 160 printf("TID %d (%c - prio %d) complete\n", t->id, groupname[t->id >> 2], 161 t->priority); 162 163 return NULL; 164 } 165 166 int main(int argc, char *argv[]) 167 { 168 int i; 169 setup(); 170 171 rt_init("hi:", parse_args, argc, argv); 172 173 if (iterations < 100) { 174 fprintf(stderr, 175 "Number of iteration cannot be less than 100.\n"); 176 exit(1); 177 } 178 179 printf("------------------------------------\n"); 180 printf("Periodic CPU Load Execution Variance\n"); 181 printf("------------------------------------\n\n"); 182 printf("Running %d iterations per thread\n", iterations); 183 printf("Thread Group A:\n"); 184 printf(" threads: %d\n", THREADS_PER_GROUP); 185 printf(" priority: %d\n", PRIO_A); 186 printf(" period: %d ms\n", PERIOD_A / NS_PER_MS); 187 printf("Thread Group B:\n"); 188 printf(" threads: %d\n", THREADS_PER_GROUP); 189 printf(" priority: %d\n", PRIO_B); 190 printf(" period: %d ms\n", PERIOD_B / NS_PER_MS); 191 printf("Thread Group C:\n"); 192 printf(" threads: %d\n", THREADS_PER_GROUP); 193 printf(" priority: %d\n", PRIO_C); 194 printf(" period: %d ms\n", PERIOD_C / NS_PER_MS); 195 printf("\n"); 196 197 for (i = 0; i < (THREADS_PER_GROUP * NUM_GROUPS); i++) { 198 stats_container_init(&dat[i], iterations); 199 stats_quantiles_init(&quantiles[i], (int)log10(iterations)); 200 } 201 202 struct periodic_arg parg_a = 203 { PERIOD_A, iterations, calc, (void *)CALC_LOOPS_A }; 204 struct periodic_arg parg_b = 205 { PERIOD_B, iterations, calc, (void *)CALC_LOOPS_B }; 206 struct periodic_arg parg_c = 207 { PERIOD_C, iterations, calc, (void *)CALC_LOOPS_C }; 208 209 for (i = 0; i < THREADS_PER_GROUP; i++) 210 create_fifo_thread(periodic_thread, (void *)&parg_a, PRIO_A); 211 for (i = 0; i < THREADS_PER_GROUP; i++) 212 create_fifo_thread(periodic_thread, (void *)&parg_b, PRIO_B); 213 for (i = 0; i < THREADS_PER_GROUP; i++) 214 create_fifo_thread(periodic_thread, (void *)&parg_c, PRIO_C); 215 216 join_threads(); 217 218 printf("\nExecution Time Statistics:\n\n"); 219 220 for (i = 0; i < (THREADS_PER_GROUP * NUM_GROUPS); i++) { 221 printf("TID %d (%c)\n", i, groupname[i >> 2]); 222 printf(" Min: %ld us\n", stats_min(&dat[i])); 223 printf(" Max: %ld us\n", stats_max(&dat[i])); 224 printf(" Avg: %f us\n", stats_avg(&dat[i])); 225 printf(" StdDev: %f us\n\n", stats_stddev(&dat[i])); 226 printf(" Quantiles:\n"); 227 stats_quantiles_calc(&dat[i], &quantiles[i]); 228 stats_quantiles_print(&quantiles[i]); 229 printf("Criteria: TID %d did not miss a period\n", i); 230 printf("Result: %s\n", fail[i] ? "FAIL" : "PASS"); 231 printf("\n"); 232 233 if (fail[i]) 234 ret = 1; 235 } 236 237 // FIXME: define pass criteria 238 // printf("\nCriteria: latencies < %d us\n", PASS_US); 239 // printf("Result: %s\n", ret ? "FAIL" : "PASS"); 240 241 for (i = 0; i < (THREADS_PER_GROUP * NUM_GROUPS); i++) { 242 stats_container_free(&dat[i]); 243 stats_quantiles_free(&quantiles[i]); 244 } 245 246 return ret; 247 } 248