#include #include #include #include #include "inc/mob.h" #include "inc/benchmark.h" #include "inc/routines.h" #include "inc/genCode.h" /* global variables externally defined */ extern struct globalSystem sys; /* local constants */ #define MIN_SIZE sizeof(int) /* minimum cache line size to check for */ #define MAX_SIZE 128 /* maximum cache line size to check for */ #define WRAP_FACTOR 8 /* maximum multiplying factor used when allocating memory array for largest caches */ #define MIN_SET M_SIZE(M_512K) /* minimum set to read between time calls */ /* local function prototypes */ static int cacheLineRun( char *array, struct cache * cache, unsigned int wrapFactor, type_t type ); static double cacheLineAccess( char *array, unsigned int size, unsigned int lineSize, type_t type ); /* Determine line size of each cache in the hierarchy. return - 0 if successful, non-zero if failed */ int cacheLine( void ){ unsigned int wrapFactor; int error, i; /* ensure block of memory large enough for cache wrap-around plus room for return block */ if( arrayPrepare( getMaxCache() * WRAP_FACTOR ) ) handleError( "checking memory in cacheLine", EnoMem ); /* detect data and instruction cache line sizes */ show( VERB_NORMAL, "Data Caches:\n" ); for( i = 1; i <= sys.numDataCaches; i++ ) { show(VERB_PLOT,"# PLOT using 6:2 title \"L%i Data Cache Line\" \n",i); if( i == sys.numDataCaches ) wrapFactor = WRAP_FACTOR; else wrapFactor = sys.dataCaches[i+1]->size / sys.dataCaches[i]->size - 1; error = cacheLineRun( sys.array, sys.dataCaches[i], wrapFactor, TYPE_DATA ); show(VERB_PLOT,"\n\n");/* Separate the sets of the output data file */ } show(VERB_NORMAL,"Instruction Caches:\n"); for( i = 1; i <= sys.numInstCaches; i++ ) { if( i == sys.numInstCaches ) wrapFactor = WRAP_FACTOR; else wrapFactor = sys.instCaches[i+1]->size / sys.instCaches[i]->size - 1; /* run it if it's not shared */ if( sys.instCaches[i]->type == TYPE_INST ){ show(VERB_PLOT,"# PLOT using 6:2 title \"L%i Instruction Cache Line\" \n",i); error = cacheLineRun( sys.array, sys.instCaches[i], wrapFactor, TYPE_INST ); show(VERB_PLOT,"\n\n");/* Separate the sets of the output data file */ } } return( error ); } /* Perform the benchmark for a particular cache (data or instruction). array - array where to perform the tests cache - cache where to test for line size wrapFactor - number of cache-size block to run through type - type of caches to check for return - 0 if successful, non-zero if failed */ static int cacheLineRun( char * array, struct cache * cache, unsigned int wrapFactor, type_t type ) { unsigned int size, minSize, numSizes; unsigned int i, trial, *votes; int elect; double *access; /* ensure cache size is defined */ if( ! cache->size ) return( -1 ); if( type == TYPE_DATA ) minSize = MIN_SIZE; else /* caluculate the minimum size depending on the size of the jump block NOTE: this keeps us from detecting cache line sizes that aren't larger that the next nearest word */ minSize = (unsigned int)ceil( ((double)jmpBlockSize()) / sizeof(int) ) * sizeof(int); /* calculate the total number of sizes considering each word aligned size from min to max */ numSizes = (MAX_SIZE - minSize) / sizeof( int ); /* allocate measurment result arrays */ access = (double *)calloc( numSizes, sizeof( double ) ); if( ! access ) handleError( "getting memory in cacheSize", EnoMem ); /* take the minimum of a number of runs */ for( trial = 1; trial <= sys.args.trials; trial++ ) /* perform the benchmark for different line sizes, sizes are incremented for each subsequent test */ for( size = minSize, i = 0; i < numSizes; size += sizeof(int), i++ ){ if( trial == 1 ) access[i] = (double)LONG_MAX; /* perform benchmark access */ access[i] = min( access[i], cacheLineAccess( array, cache->size * wrapFactor, size, type ) ); if( trial == sys.args.trials ){ show( VERB_INSPECT, "Time: %.4f ns [size %lu]\n", access[i],size); show( VERB_PLOT, "Time: %.4f ns [ size %lu ]\n", access[i],size); } } /* try to detect a plateau at the correct cache line size */ votes = detectPlateau2( access, numSizes ); if( ! votes ) handleError( "getting memory for plateau detection", EnoMem ); /* choose the point with the most votes */ elect = getPopular( votes, numSizes ); if( elect > 0 ) { cache->lineSize = minSize + (elect * sizeof(int)); show( VERB_NORMAL, "Elected line size: %d\n", cache->lineSize ); } else { cache->lineSize = 0; show( VERB_NORMAL, "None elected.\n" ); } /* free acquired memory */ free( access ); free( votes ); return( 0 ); } /* Run line size benchmark for data or instruction caches. array - memory to access size - size to access in bytes lineSize - line size to test type - type of cache to benchmark return - result of benchmark of trial */ static double cacheLineAccess( char *array, unsigned int size, unsigned int lineSize, type_t type ) { unsigned int sets; double result; /* compute number of sets to perform */ sets = maxVal( (MIN_SET * lineSize) / size, 1 ); /* perform the appropriate benchmark test */ if( type == TYPE_DATA ) result = dataRead( array, size, lineSize, sets ); else result = instExecute( array, size, lineSize, sets ); /* return the average result per operation in ns */ return( result ); } /* Determine whether the current data is valid for a given benchmark. If it is valid then another run is unecessary unless explicitly requested. Will return valid if: - Every cache (Data or Inst) has a non-zero line-size. return - 1 if valid, 0 otherwise */ int cacheLineValid( void ) { int i; for( i = 1; i <= sys.numDataCaches; i++ ) if( ! sys.dataCaches[i]->lineSize ) return( 0 ); for( i = 1; i <= sys.numInstCaches; i++ ) if( ! sys.instCaches[i]->lineSize ) return( 0 ); return( sys.numDataCaches || sys.numInstCaches ); }