/** file papi_multiplex_cost.c
* @brief papi_multiplex_cost utility.
* @page papi_multiplex_cost
* @section NAME
* papi_multiplex_cost - computes execution time costs for basic PAPI operations on multiplexed EventSets.
*
* @section Synopsis
* papi_cost [-m, --min < min >] [-x, --max < max >] [-k,-s]
*
* @section Description
* papi_multiplex_cost is a PAPI utility program that computes the
* min / max / mean / std. deviation of execution times for PAPI start/stop
* pairs and for PAPI reads on multiplexed eventsets.
* This information provides the basic operating cost to a user's program
* for collecting hardware counter data.
* Command line options control display capabilities.
*
* @section Options
* <ul>
* <li>-m < Min number of events to test >
* <li>-x < Max number of events to test >
* <li> -k, Do not time kernel multiplexing
* <li> -s, Do not ime software multiplexed EventSets
* <li> -t THREASHOLD, Test with THRESHOLD iterations of counting loop.
* </ul>
*
* @section Bugs
* There are no known bugs in this utility. If you find a bug,
* it should be reported to the PAPI Mailing List at <ptools-perfapi@icl.utk.edu>.
*/
/* Open Issues:
* Selecting events to add is very primitive right now.
* Output format, right now the format targets a gnuplot script I have,
* We will probably end up generating a csv per test
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "papi.h"
#include "cost_utils.h"
static int first_time = 1;
static int skip = 0;
static FILE* fp;
typedef struct {
int first_time;
int force_sw;
int kernel_mpx;
int min;
int max;
} options_t;
static options_t options;
void
do_output( char *fn, char *message, long long* array, int noc )
{
long long min, max;
double average, std;
std = do_stats( array, &min, &max, &average );
if ( first_time ) {
skip = 0;
fp = fopen(fn, "w");
if (fp == NULL) {
fprintf(stderr,"Unable to open output file, %s, output will not be saved.\n", fn);
skip = 1;
} else
fprintf(fp, "###%s\n#number of events\tmin cycles\tmax cycles\tmean cycles\t\
std deviation\tsw min cycles\tsw max cycles\tsw avg cycles\tsw std dev\n", message);
first_time = 0;
}
if ( !skip ) {
fprintf(fp, "%20d\t%10lld\t%10lld\t%10lf\t%10lf", noc, min, max, average, std);
std = do_stats( array+num_iters, &min, &max, &average );
fprintf(fp, "\t%10lld\t%10lld\t%10lf\t%10lf\n", min, max, average, std);
fflush(fp);
}
}
void
init_test(int SoftwareMPX, int KernelMPX, int* Events)
{
int i;
int retval;
PAPI_option_t option, itimer;
retval = PAPI_assign_eventset_component( SoftwareMPX, 0 );
if (retval != PAPI_OK ) {
fprintf(stderr,"Error! PAPI_assign_eventset_component\n");
exit(retval);
}
retval = PAPI_assign_eventset_component( KernelMPX, 0 );
if (retval != PAPI_OK ) {
fprintf(stderr,"Error! PAPI_assign_eventset_component\n");
exit(retval);
}
retval = PAPI_set_multiplex( KernelMPX );
if (retval != PAPI_OK ) {
fprintf(stderr,"Error! PAPI_set_multiplex\n");
exit(retval);
}
PAPI_get_opt(PAPI_DEF_ITIMER,&itimer);
memset(&option,0x0,sizeof(option));
option.multiplex.flags = PAPI_MULTIPLEX_FORCE_SW;
option.multiplex.eventset = SoftwareMPX;
option.multiplex.ns = itimer.itimer.ns;
retval = PAPI_set_opt( PAPI_MULTIPLEX, &option );
if (retval != PAPI_OK ) {
fprintf(stderr,"Error! PAPI_set_opt\n");
exit(retval);
}
for (i = 0; i < options.min - 1; i++) {
if ( options.kernel_mpx ) {
retval = PAPI_add_event( KernelMPX, Events[i]);
if (retval != PAPI_OK ) {
fprintf(stderr,"Error! PAPI_add_event\n");
exit(retval);
}
}
if ( options.force_sw ) {
retval = PAPI_add_event( SoftwareMPX, Events[i]);
if (retval != PAPI_OK ) {
fprintf(stderr,"Error! PAPI_add_event\n");
exit(retval);
}
}
}
}
void
finalize_test(void)
{
if (fp) fclose(fp);
first_time = 1;
}
static void
usage(void)
{
printf( "Usage: papi_multiplex_cost [options]\n"
"\t-m num, number of events to count\n"
"\t-x num, number of events to count\n"
"\t-s, Do not run software multiplexing test.\n"
"\t-k, Do not attempt kernel multiplexed test.\n"
"\t-t THREASHOLD set the threshold for the number "
"of iterations. Default: 100,000\n" );
}
int
main( int argc, char **argv )
{
int retval, retval_start, retval_stop;
int KernelMPX = PAPI_NULL;
int SoftwareMPX = PAPI_NULL;
int *Events = NULL;
int number_of_counters;
int i;
int c;
int dont_loop_forever;
long long totcyc, *values = NULL;
long long *array = NULL;
int event;
PAPI_option_t option, itimer;
const PAPI_component_info_t *info;
PAPI_set_debug(PAPI_QUIET);
options.min = 1;
options.max = 10;
options.force_sw = 1;
options.kernel_mpx = 1;
while ( ( c=getopt(argc, argv, "hm:x:skt:") ) != -1 ) {
switch (c) {
case 'h':
usage();
exit(0);
case 'm':
options.min = atoi(optarg);
break;
case 'x':
options.max = atoi(optarg);
break;
case 's':
options.force_sw = 0;
break;
case 'k':
options.kernel_mpx = 0;
break;
case 't':
num_iters = atoi(optarg);
default:
break;
}
}
printf("This utility benchmarks the overhead of PAPI multiplexing\n");
printf("Warning! This can take a long time (many minutes) to run\n");
printf("The output goes to multiple .dat files in the current directory\n\n");
if ( options.min > options.max ) {
fprintf(stderr,"Error! Min # of Events > Max # of Events");
goto cleanup;
}
values = (long long*)malloc(sizeof(long long) * options.max);
array = (long long *)malloc(sizeof(long long) * 2 * num_iters);
Events = ( int* )malloc(sizeof(int) * options.max);
retval = PAPI_library_init( PAPI_VER_CURRENT );
if (retval != PAPI_VER_CURRENT ) {
fprintf(stderr, "Error! PAPI_library_init\n");
exit(retval);
}
retval = PAPI_set_debug( PAPI_QUIET );
if (retval != PAPI_OK ) {
fprintf(stderr,"Error! PAPI_set_debug\n");
exit(retval );
}
retval = PAPI_multiplex_init( );
if (retval != PAPI_OK ) {
fprintf(stderr,"Error! PAPI_multiplex_init\n");
exit(retval);
}
info = PAPI_get_component_info(0);
options.kernel_mpx &= info->kernel_multiplex;
if ( options.kernel_mpx && !info->kernel_multiplex ) {
fprintf(stderr,"Error! Kernel multiplexing is "
"not supported on this platform, bailing!\n");
exit(1);
}
retval = PAPI_create_eventset( &SoftwareMPX );
if (retval != PAPI_OK) {
fprintf(stderr,"Error! PAPI_create_eventset\n");
exit(retval);
}
retval = PAPI_create_eventset( &KernelMPX );
if (retval != PAPI_OK ) {
fprintf(stderr,"PAPI_create_eventset");
exit(retval);
}
retval = PAPI_assign_eventset_component( KernelMPX, 0 );
if (retval != PAPI_OK ) {
fprintf(stderr,"PAPI_assign_eventset_component");
exit(retval);
}
retval = PAPI_set_multiplex( KernelMPX );
if (retval != PAPI_OK ) {
fprintf(stderr,"PAPI_set_multiplex");
exit(retval);
}
retval = PAPI_assign_eventset_component( SoftwareMPX, 0 );
if (retval != PAPI_OK ) {
fprintf(stderr,"PAPI_assign_eventset_component");
exit(retval);
}
PAPI_get_opt(PAPI_DEF_ITIMER,&itimer);
memset(&option,0x0,sizeof(option));
option.multiplex.flags = PAPI_MULTIPLEX_FORCE_SW;
option.multiplex.eventset = SoftwareMPX;
option.multiplex.ns = itimer.itimer.ns;
retval = PAPI_set_opt( PAPI_MULTIPLEX, &option );
if (retval != PAPI_OK) {
fprintf(stderr,"PAPI_set_opt");
exit(retval);
}
if ( !options.kernel_mpx && !options.force_sw ) {
fprintf(stderr,"No tests to run.");
goto cleanup;
} else {
fprintf(stderr,"Running test[s]\n");
if (options.kernel_mpx)
fprintf(stderr,"\tKernel multiplexing read\n");
if (options.force_sw)
fprintf(stderr,"\tSoftware Multiplexing read\n");
}
event = 0 | PAPI_NATIVE_MASK;
PAPI_enum_event( &event, PAPI_ENUM_FIRST );
/* Find some events to run the tests with. */
for (number_of_counters = 0; number_of_counters < options.max; number_of_counters++) {
dont_loop_forever = 0;
if ( options.kernel_mpx ) {
do {
PAPI_enum_event( &event, PAPI_ENUM_EVENTS );
dont_loop_forever++;
} while ( ( retval = PAPI_add_event( KernelMPX, event ) ) != PAPI_OK &&
dont_loop_forever < 512);
} else {
do {
PAPI_enum_event( &event, PAPI_ENUM_EVENTS );
dont_loop_forever++;
} while ( ( retval = PAPI_add_event( SoftwareMPX, event) ) != PAPI_OK &&
dont_loop_forever < 512);
}
if ( dont_loop_forever == 512 )
fprintf(stderr,"I can't find %d events to count at once.", options.max);
Events[number_of_counters] = event;
}
PAPI_cleanup_eventset( KernelMPX );
PAPI_cleanup_eventset( SoftwareMPX );
/* Start/Stop test */
init_test(SoftwareMPX, KernelMPX, Events);
for (number_of_counters = options.min; number_of_counters < options.max; number_of_counters++) {
if ( options.kernel_mpx ) {
if ( ( retval = PAPI_add_event( KernelMPX, Events[number_of_counters - options.min] ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_add_event");
goto cleanup;
}
if ( ( retval = PAPI_start( KernelMPX ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_start");
exit(retval);
}
if ( ( retval = PAPI_stop( KernelMPX, values ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_stop");
exit(retval);
}
/* KernelMPX Timing loop */
for ( i = 0; i < num_iters; i++ ) {
totcyc = PAPI_get_real_cyc();
retval_start=PAPI_start( KernelMPX );
retval_stop=PAPI_stop( KernelMPX, values );
array[i] = PAPI_get_real_cyc() - totcyc;
if (retval_start || retval_stop)
fprintf(stderr,"PAPI start/stop");
} /* End 1 timing run */
} else
memset(array, 0, sizeof(long long) * num_iters );
/* Also test software multiplexing */
if ( options.force_sw ) {
if ( ( retval = PAPI_add_event( SoftwareMPX, Events[number_of_counters - options.min] ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_add_event");
goto cleanup;
}
if ( ( retval = PAPI_start( SoftwareMPX ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_start");
exit(retval);
}
if ( ( retval = PAPI_stop( SoftwareMPX, values ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_stop");
exit(retval);
}
/* SoftwareMPX Timing Loop */
for ( i = num_iters; i < 2*num_iters; i++ ) {
totcyc = PAPI_get_real_cyc();
retval_start=PAPI_start( SoftwareMPX );
retval_stop=PAPI_stop( SoftwareMPX, values );
array[i] = PAPI_get_real_cyc() - totcyc;
if (retval_start || retval_stop)
fprintf(stderr,"PAPI start/stop");
} /* End 2 timing run */
} else {
memset(array+num_iters, 0, sizeof(long long) * num_iters );
}
do_output( "papi_startstop.dat", "Multiplexed PAPI_read()", array, number_of_counters );
} /* End counter loop */
PAPI_cleanup_eventset( SoftwareMPX );
PAPI_cleanup_eventset( KernelMPX );
finalize_test();
/* PAPI_read() test */
init_test(SoftwareMPX, KernelMPX, Events);
for (number_of_counters = options.min; number_of_counters < options.max; number_of_counters++) {
if ( options.kernel_mpx ) {
if ( ( retval = PAPI_add_event( KernelMPX, Events[number_of_counters - options.min] ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_add_event");
goto cleanup;
}
if ( ( retval = PAPI_start( KernelMPX ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_start");
exit(retval);
}
PAPI_read( KernelMPX, values );
/* KernelMPX Timing loop */
for ( i = 0; i < num_iters; i++ ) {
totcyc = PAPI_get_real_cyc();
retval = PAPI_read( KernelMPX, values );
array[i] = PAPI_get_real_cyc() - totcyc;
} /* End 1 timing run */
retval_stop=PAPI_stop( KernelMPX, values );
if (retval_stop!=PAPI_OK)
fprintf(stderr,"PAPI_stop");
} else
memset(array, 0, sizeof(long long) * num_iters );
/* Also test software multiplexing */
if ( options.force_sw ) {
if ( ( retval = PAPI_add_event( SoftwareMPX, Events[number_of_counters - options.min] ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_add_event");
goto cleanup;
}
if ( ( retval = PAPI_start( SoftwareMPX ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_start");
exit(retval);
}
PAPI_read( SoftwareMPX, values );
/* SoftwareMPX Timing Loop */
for ( i = num_iters; i < 2*num_iters; i++ ) {
totcyc = PAPI_get_real_cyc();
retval = PAPI_read( SoftwareMPX, values );
array[i] = PAPI_get_real_cyc() - totcyc;
} /* End 2 timing run */
retval_stop=PAPI_stop( SoftwareMPX, values );
if (retval_stop!=PAPI_OK)
fprintf(stderr,"PAPI_stop");
} else
memset(array+num_iters, 0, sizeof(long long) * num_iters );
do_output( "papi_read.dat", "Multiplexed PAPI_read()", array, number_of_counters );
} /* End counter loop */
PAPI_cleanup_eventset( SoftwareMPX );
PAPI_cleanup_eventset( KernelMPX );
finalize_test();
/* PAPI_read_ts() test */
init_test( SoftwareMPX, KernelMPX, Events);
for (number_of_counters = options.min; number_of_counters < options.max; number_of_counters++) {
if ( options.kernel_mpx ) {
if ( (retval = PAPI_add_event( KernelMPX, Events[number_of_counters - options.min] ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_add_event");
goto cleanup;
}
if ( ( retval = PAPI_start( KernelMPX ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_start");
exit(retval);
}
PAPI_read_ts( KernelMPX, values, &totcyc );
/* KernelMPX Timing loop */
for ( i = 0; i < num_iters; i++ ) {
retval = PAPI_read_ts( KernelMPX, values, &array[i] );
} /* End 1 timing run */
/* post-process the timing array */
for ( i = num_iters - 1; i > 0; i-- ) {
array[i] -= array[i - 1];
}
array[0] -= totcyc;
retval_stop=PAPI_stop( KernelMPX, values );
if (retval_stop!=PAPI_OK)
fprintf(stderr,"PAPI_stop");
} else
memset(array, 0, sizeof(long long) * num_iters );
/* Also test software multiplexing */
if ( options.force_sw ) {
if ( ( retval = PAPI_add_event( SoftwareMPX, Events[number_of_counters - options.min] ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_add_event");
goto cleanup;
}
if ( ( retval = PAPI_start( SoftwareMPX ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_start");
exit(retval);
}
PAPI_read_ts( SoftwareMPX, values, &totcyc);
/* SoftwareMPX Timing Loop */
for ( i = num_iters; i < 2*num_iters; i++ ) {
retval = PAPI_read_ts( SoftwareMPX, values, &array[i]);
} /* End 2 timing run */
retval_stop=PAPI_stop( SoftwareMPX, values );
if (retval_stop!=PAPI_OK)
fprintf(stderr,"PAPI_stop");
/* post-process the timing array */
for ( i = 2*num_iters - 1; i > num_iters; i-- ) {
array[i] -= array[i - 1];
}
array[num_iters] -= totcyc;
} else
memset(array+num_iters, 0, sizeof(long long) * num_iters );
do_output( "papi_read_ts.dat", "Multiplexed PAPI_read_ts()", array, number_of_counters );
} /* End counter loop */
PAPI_cleanup_eventset( SoftwareMPX );
PAPI_cleanup_eventset( KernelMPX );
finalize_test();
/* PAPI_accum() test */
init_test(SoftwareMPX, KernelMPX, Events);
for (number_of_counters = options.min; number_of_counters < options.max; number_of_counters++) {
if ( options.kernel_mpx ) {
if ( ( retval = PAPI_add_event( KernelMPX, Events[number_of_counters - options.min] ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_add_event");
goto cleanup;
}
if ( ( retval = PAPI_start( KernelMPX ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_start");
exit(retval);
}
PAPI_read( KernelMPX, values );
/* KernelMPX Timing loop */
for ( i = 0; i < num_iters; i++ ) {
totcyc = PAPI_get_real_cyc();
retval = PAPI_accum( KernelMPX, values );
array[i] = PAPI_get_real_cyc() - totcyc;
} /* End 1 timing run */
retval_stop=PAPI_stop( KernelMPX, values );
if (retval_stop!=PAPI_OK)
fprintf(stderr,"PAPI_stop");
} else {
memset(array, 0, sizeof(long long) * num_iters );
}
/* Also test software multiplexing */
if ( options.force_sw ) {
if ( ( retval = PAPI_add_event( SoftwareMPX, Events[number_of_counters - options.min] ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_add_event");
goto cleanup;
}
if ( ( retval = PAPI_start( SoftwareMPX ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_start");
exit(retval);
}
PAPI_read( SoftwareMPX, values );
/* SoftwareMPX Timing Loop */
for ( i = num_iters; i < 2*num_iters; i++ ) {
totcyc = PAPI_get_real_cyc();
retval = PAPI_accum( SoftwareMPX, values );
array[i] = PAPI_get_real_cyc() - totcyc;
} /* End 2 timing run */
retval_stop=PAPI_stop( SoftwareMPX, values );
if (retval_stop!=PAPI_OK)
fprintf(stderr,"PAPI_stop");
} else {
memset(array+num_iters, 0, sizeof(long long) * num_iters );
}
do_output( "papi_accum.dat", "Multiplexed PAPI_accum()", array, number_of_counters );
} /* End counter loop */
PAPI_cleanup_eventset( SoftwareMPX );
PAPI_cleanup_eventset( KernelMPX );
finalize_test();
/* PAPI_reset() test */
init_test(SoftwareMPX, KernelMPX, Events);
for (number_of_counters = options.min; number_of_counters < options.max; number_of_counters++) {
if ( options.kernel_mpx ) {
if ( ( retval = PAPI_add_event( KernelMPX, Events[number_of_counters - options.min] ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_add_event");
goto cleanup;
}
if ( ( retval = PAPI_start( KernelMPX ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_start");
exit(retval);
}
PAPI_read( KernelMPX, values );
/* KernelMPX Timing loop */
for ( i = 0; i < num_iters; i++ ) {
totcyc = PAPI_get_real_cyc();
retval = PAPI_reset( KernelMPX );
array[i] = PAPI_get_real_cyc() - totcyc;
} /* End 1 timing run */
retval_stop=PAPI_stop( KernelMPX, values );
if (retval_stop!=PAPI_OK)
fprintf(stderr,"PAPI_stop");
} else
memset(array, 0, sizeof(long long) * num_iters );
/* Also test software multiplexing */
if ( options.force_sw ) {
if ( ( retval = PAPI_add_event( SoftwareMPX, Events[number_of_counters - options.min] ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_add_event");
goto cleanup;
}
if ( ( retval = PAPI_start( SoftwareMPX ) ) != PAPI_OK ) {
fprintf(stderr,"PAPI_start");
exit(retval);
}
PAPI_read( SoftwareMPX, values );
/* SoftwareMPX Timing Loop */
for ( i = num_iters; i < 2*num_iters; i++ ) {
totcyc = PAPI_get_real_cyc();
retval = PAPI_reset( SoftwareMPX );
array[i] = PAPI_get_real_cyc() - totcyc;
} /* End 2 timing run */
retval_stop=PAPI_stop( SoftwareMPX, values );
if (retval_stop!=PAPI_OK)
fprintf(stderr,"PAPI_stop");
} else {
memset(array+num_iters, 0, sizeof(long long) * num_iters );
}
do_output( "papi_reset.dat", "Multiplexed PAPI_reset()", array, number_of_counters );
} /* End counter loop */
PAPI_cleanup_eventset( SoftwareMPX );
PAPI_cleanup_eventset( KernelMPX );
finalize_test();
return 0;
cleanup:
if ( KernelMPX != PAPI_NULL) PAPI_cleanup_eventset( KernelMPX );
if ( SoftwareMPX != PAPI_NULL ) PAPI_cleanup_eventset( KernelMPX );
if ( values != NULL ) free(values);
if ( array != NULL ) free(array);
if ( Events != NULL ) free(Events);
PAPI_shutdown();
return 1;
}