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@us.ibm.com>
31 *
32 * HISTORY
33 * 2007-April-27: Initial version by Darren Hart <dvhltc@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
usage(void)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
parse_args(int c,char * v)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
calc(void * arg)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
periodic_thread(void * thread)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
main(int argc,char * argv[])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