blob: a3b67bf87bd6f459a3b9156672753129400fecef [file] [log] [blame]
/******************************************************************************
*
* Copyright © International Business Machines Corp., 2006, 2008
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* NAME
* periodic_cpu_load.c
*
* DESCRIPTION
* Measure variation in computational execution time
* at various periods and priorities.
*
* USAGE:
* Use run_auto.sh script in current directory to build and run test.
*
* AUTHOR
* Darren Hart <dvhltc@us.ibm.com>
*
* HISTORY
* 2007-April-27: Initial version by Darren Hart <dvhltc@us.ibm.com>
*
* This line has to be added to avoid a stupid CVS problem
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <librttest.h>
#include <libstats.h>
#define PRIO_A 63
#define PRIO_B 53
#define PRIO_C 43
#define PERIOD_A 40*NS_PER_MS
#define PERIOD_B 80*NS_PER_MS
#define PERIOD_C 160*NS_PER_MS
#define CALC_LOOPS_A (27*100)
#define CALC_LOOPS_B (50*100)
#define CALC_LOOPS_C (72*100)
#define NUM_GROUPS 3
#define THREADS_PER_GROUP 4
//#define ITERATIONS 100 /* short functional test run */
#define ITERATIONS 6000 /* about 15 minutes @ 2GHz on 1 CPU */
//#define ITERATIONS 1000 /* min iters for 3 nines */
// FIXME: need some kind of passing criteria calculation
//#define PASS_US 100
int fail[THREADS_PER_GROUP * NUM_GROUPS];
stats_container_t dat[THREADS_PER_GROUP * NUM_GROUPS];
stats_record_t rec;
stats_quantiles_t quantiles[THREADS_PER_GROUP * NUM_GROUPS];
static const char groupname[NUM_GROUPS] = "ABC";
static int iterations = ITERATIONS;
static int ret = 0;
void usage(void)
{
rt_help();
printf("periodic_cpu_load specific options:\n");
printf(" -iITERATIONS number of iterations to calculate the average over\n");
}
int parse_args(int c, char *v)
{
int handled = 1;
switch (c) {
break;
case 'i':
iterations = atoi(v);
break;
case 'h':
usage();
exit(0);
default:
handled = 0;
break;
}
return handled;
}
struct periodic_arg {
int period;
int iterations;
void*(*func)(void*);
void *arg;
};
void *calc(void *arg)
{
int i, j;
int loops = (intptr_t)arg;
for (i = 0; i < loops; i++) {
for (j = 0; j < 125; j++) {
// Sum of the numbers up to J
int temp = j * ( j + 1 ) / 2;
(void)temp;
}
}
return NULL;
}
void *periodic_thread(void *thread)
{
struct thread *t = (struct thread *)thread;
struct periodic_arg *parg = (struct periodic_arg *)t->arg;
nsec_t period = parg->period;
void*(*func)(void*) = parg->func;
int i = 0;
nsec_t next, now;
nsec_t exe_start, exe_end, exe_time;
next = rt_gettime();
while (i < parg->iterations) {
next += period;
if (rt_gettime() > next) {
printf("TID %d missed period, aborting\n", t->id);
fail[t->id] = 1;
break;
}
exe_start = rt_gettime();
func(parg->arg);
exe_end = rt_gettime();
exe_time = exe_end - exe_start;
rec.x = i;
rec.y = exe_time/NS_PER_US;
stats_container_append(&dat[t->id], rec);
i++;
now = rt_gettime();
if (now > next) {
printf("Missed period, aborting (calc took too long)\n");
fail[t->id] = 1;
break;
}
rt_nanosleep(next - now);
}
printf("TID %d (%c - prio %d) complete\n", t->id, groupname[t->id>>2],
t->priority);
return NULL;
}
int main(int argc, char *argv[])
{
int i;
setup();
rt_init("hi:", parse_args, argc, argv);
if (iterations < 100) {
fprintf(stderr, "Number of iteration cannot be less than 100.\n");
exit(1);
}
printf("------------------------------------\n");
printf("Periodic CPU Load Execution Variance\n");
printf("------------------------------------\n\n");
printf("Running %d iterations per thread\n", iterations);
printf("Thread Group A:\n");
printf(" threads: %d\n", THREADS_PER_GROUP);
printf(" priority: %d\n", PRIO_A);
printf(" period: %d ms\n", PERIOD_A/NS_PER_MS);
printf("Thread Group B:\n");
printf(" threads: %d\n", THREADS_PER_GROUP);
printf(" priority: %d\n", PRIO_B);
printf(" period: %d ms\n", PERIOD_B/NS_PER_MS);
printf("Thread Group C:\n");
printf(" threads: %d\n", THREADS_PER_GROUP);
printf(" priority: %d\n", PRIO_C);
printf(" period: %d ms\n", PERIOD_C/NS_PER_MS);
printf("\n");
for (i=0; i<(THREADS_PER_GROUP * NUM_GROUPS); i++) {
stats_container_init(&dat[i], iterations);
stats_quantiles_init(&quantiles[i], (int)log10(iterations));
}
struct periodic_arg parg_a = {PERIOD_A, iterations, calc, (void *)CALC_LOOPS_A };
struct periodic_arg parg_b = {PERIOD_B, iterations, calc, (void *)CALC_LOOPS_B };
struct periodic_arg parg_c = {PERIOD_C, iterations, calc, (void *)CALC_LOOPS_C };
for (i=0; i < THREADS_PER_GROUP; i++)
create_fifo_thread(periodic_thread, (void*)&parg_a, PRIO_A);
for (i=0; i < THREADS_PER_GROUP; i++)
create_fifo_thread(periodic_thread, (void*)&parg_b, PRIO_B);
for (i=0; i < THREADS_PER_GROUP; i++)
create_fifo_thread(periodic_thread, (void*)&parg_c, PRIO_C);
join_threads();
printf("\nExecution Time Statistics:\n\n");
for (i=0; i<(THREADS_PER_GROUP * NUM_GROUPS); i++) {
printf("TID %d (%c)\n", i, groupname[i>>2]);
printf(" Min: %ld us\n", stats_min(&dat[i]));
printf(" Max: %ld us\n", stats_max(&dat[i]));
printf(" Avg: %f us\n", stats_avg(&dat[i]));
printf(" StdDev: %f us\n\n", stats_stddev(&dat[i]));
printf(" Quantiles:\n");
stats_quantiles_calc(&dat[i], &quantiles[i]);
stats_quantiles_print(&quantiles[i]);
printf("Criteria: TID %d did not miss a period\n", i);
printf("Result: %s\n", fail[i] ? "FAIL":"PASS");
printf("\n");
if (fail[i])
ret = 1;
}
// FIXME: define pass criteria
// printf("\nCriteria: latencies < %d us\n", PASS_US);
// printf("Result: %s\n", ret ? "FAIL" : "PASS");
for (i=0; i<(THREADS_PER_GROUP * NUM_GROUPS); i++) {
stats_container_free(&dat[i]);
stats_quantiles_free(&quantiles[i]);
}
return ret;
}