• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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