/** * Benchmark to determine the associativity of each cache. **/ #include #include #include #include "inc/mob.h" #include "inc/routines.h" /* global variables externally defined */ extern struct globalSystem sys; #define MIN_SETS 256 /* minimum blocks to read between gettimeofday() calls */ #define WRAP_FACTOR 2 /* multiplicative factor used to allocate enough memory */ #define MID_FACTOR (4.0F/3.0F) /* intermediate test at 4/3 cache size */ /* local function prototypes */ static int cacheWriteRun( char *array, struct cache *cache ); static void cacheWritePrepare( char *access, unsigned int size, unsigned int strides ); /* Determine write policy for each data cache in the hierarchy. return - 0 if successful, non-zero if failed */ int cacheWrite( void ) { int error, i; if( sys.numDataCaches ) { /* ensure block of memory large enough for cache wrap-around */ if( arrayPrepare( getMaxCache() * WRAP_FACTOR ) ) handleError( "getting memory in cacheWrite", EnoMem ); } /* detect data cache write policies */ show( VERB_NORMAL, "Data Caches:\n" ); for( i = 1; i <= sys.numDataCaches; i++ ) error = cacheWriteRun( sys.array, sys.dataCaches[i] ); return( error ); } /* Perform the benchmark for a particular cache set. array - memory used to perform tests cache - cache to test return - 0 if successful, non-zero if failed */ static int cacheWriteRun( char *array, struct cache *cache ) { unsigned int trial; double low, hi, hit, miss; char *mode; /* ensure the line size is defined */ if( ! cache->lineSize ) return( -1 ); /* set artificially high for the minimum */ low = hi = hit = miss = (double)LONG_MAX; /* take the minimum of a number of trials */ for( trial = 1; trial <= sys.args.trials; trial++ ) { /* perform the benchmark saving the minimum value across trials */ /* 1. the minimum time to do writes when the values are already in cache */ cacheWritePrepare( array, cache->size, cache->lineSize ); low = min( low, dataWrite( array, cache->size, cache->lineSize, MIN_SETS ) ); /* 2. a maximum time where we do a write for each access plus possible misses */ hi = min( hi, dataWrite( array, cache->size * WRAP_FACTOR, cache->lineSize, MIN_SETS ) ); /* 3. determine minimum hit and miss times as a guage */ hit = min( hit, dataRead( array, cache->size, cache->lineSize, MIN_SETS ) ); miss = min( miss, dataRead( array, cache->size * WRAP_FACTOR, cache->lineSize, MIN_SETS ) ); if( trial < sys.args.trials ) show( VERB_DEBUG, "Trial: %u, Low: %.4f ns ns Hi: %.4f\n", trial, low, hi ); else show( VERB_INSPECT, "Hit: %.4f Miss %.4f Low: %.4f ns ns Hi: %.4f\n", hit, miss, low, hi ); } cache->writeMode = WR_NONE; /* decide which pattern matches we depend on the fact that no-alloc/back is not valid no-alloc/through should have equal times for both tests */ if( hi < low + hit ) cache->writeMode = WR_THROUGH_NOALLOC; /* we use the read miss time to distinguish between hits and writes */ else if( low < miss ) /* must be write-back */ cache->writeMode = WR_BACK_ALLOC; else /* must be write through */ cache->writeMode = WR_THROUGH_ALLOC; /* print results */ switch( cache->writeMode ) { case WR_BACK_ALLOC: mode = "write-back/allocate"; break; case WR_BACK_NOALLOC: mode = "write-back/no-allocate"; break; case WR_THROUGH_ALLOC: mode = "write-through/allocate"; break; case WR_THROUGH_NOALLOC: mode = "write-through/no-allocate"; break; default: mode = NULL; } if( mode ) show( VERB_NORMAL, "Found L%u replacement policy is %s\n", cache->level, mode ); else show( VERB_NORMAL, "Didn't detect L%u replacement policy\n", cache->level ); return( 0 ); } /* Prepare for cache write test by pre-reading a section. array - memory to read size - total size of memory to cover stride - size of stride between reads */ static void cacheWritePrepare( char *array, unsigned int size, unsigned int stride ) { char phony; unsigned int s, strides; char *mem; /* calculate the number of strides */ strides = size / stride; /* start one stride behind so that first jump hits first byte */ mem = array - stride; /* read a value at each stride position */ for( s = 0; s < strides; s++ ) phony = *(mem += stride); } /* 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: - At least one cache exists - Every cache (Data or Inst) has a defined write policy. return - 1 if valid, 0 otherwise */ int cacheWriteValid( void ) { int i; for( i = 1; i <= sys.numDataCaches; i++ ) if( sys.dataCaches[i]->writeMode == WR_NONE ) return( 0 ); return( sys.numDataCaches ); }