#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; /* total number of page sizes to check */ #define NUM_TLB_PAGE_SIZES 16 /* minimum page size to check for */ #define MIN_TLB_PAGE_SIZE M_SIZE(M_256) /* maximum page size to check for, each page is twice as large as previous */ #define MAX_TLB_PAGE_SIZE (MIN_TLB_PAGE_SIZE << (NUM_TLB_PAGE_SIZES-1)) /* Maximum size we can get, to maximize the number of reads/jumps in every test */ #define WORKING_SIZE M_SIZE(M_32M) /* Force a minimum of conflict misses for all page sizes that we try. For high page sizes, and since memory is limited (without using vm), there are only a few possible jumps within the allocated memory (ex: trying 4M pages, if we have allocated only 32M of memory we can only test 8 positions) So with this variable we try to force at least a minimum of conflict misses (in cache as well as in TLB) so that all results are comparable. NOTE: that the we cannot assure it but at least we try...*/ /* WARNING It has to be >= STRAIGHT if we use the function dataRead !! */ #define MIN_ASSOC_CONFLICTS 32 #define MIN_SET M_SIZE(M_512K) /* minimum size to access between time calls */ /* local function prototypes */ static int tlbPageRun( char * array, unsigned int maxMem, unsigned int maxPage, type_t type ); static double tlbPageAccess( char *array, unsigned int size, unsigned int stride, type_t type ); #if 0 size_t getAvailUserMem(){ /* temporal hack...before actually adding the test to detect it...*/ return( WORKING_SIZE ); } #endif /* Determine page size of each TLB. return - 0 if successful, non-zero if failed */ int tlbPage( void ){ int error = 0; unsigned int maxPage; /* Get the working set available to us (limited to WORKING SIZE) */ size_t workSize = minVal( sys.arraySize, WORKING_SIZE ); /* ensure block of memory large enough for working set */ if( arrayPrepare( workSize ) ) handleError( "checking memory in tlbPage", EnoMem ); show(VERB_DEBUG,"Working Size for TLB Page %d bytes\n",(unsigned int)workSize); maxPage=MAX_TLB_PAGE_SIZE; while( maxPage * MIN_ASSOC_CONFLICTS *2 > workSize ){ /* Reduce the maximum size to choose from depending on the max memory we have*/ maxPage>>=1; }; show(VERB_DEBUG,"Maximum Page Size tested: %u \n",maxPage); /* Call tlb detection for data and instructions */ show( VERB_NORMAL, "Data TLBs:\n" ); show(VERB_PLOT,"# PLOT using 6:2 title \"Data TLB Page Size\" \n"); error = tlbPageRun( sys.array, workSize, maxPage,TYPE_DATA ); show( VERB_NORMAL, "Instruction TLBs:\n" ); show(VERB_PLOT,"\n\n# PLOT using 6:2 title \"Instruction TLB Page Size\" \n"); error += tlbPageRun( sys.array, workSize, maxPage,TYPE_INST ); show(VERB_PLOT,"\n\n");/* Separate the sets of the output data file */ return( error ); } /* Finds the different TLB's of a type (data or inst) in the system. NOTE: For the small sizes, the test could be optimized by not reading the maximum amount of available memory, since they proportionally do a lot more of accesses...the values though, have to be carefully chosen since we want to make sure that we bust the tlb and cache associativity... array: array where to read or execute from maxMem: amount of memory allocated for the maxPage test maxPage: max page size to test (in bytes) type: indicates if we should test for data or instruction TLB returns 0 if successful or <>0 otherwise. */ static int tlbPageRun( char *array, unsigned int maxMem, unsigned int maxPage, type_t type ) { double * access; unsigned int i, trial, size; int selected; struct tlb ** tlb; access = (double *)calloc( NUM_TLB_PAGE_SIZES, sizeof(double) ); if( access == NULL ) handleError( "getting access array in tlbPageRun", EnoMem); /* take the minimum of a number of runs */ for( trial = 1; trial <= sys.args.trials; trial++ ){ /* perform the benchmark for different TLB page sizes, sizes are in powers of 2 */ for( size = MIN_TLB_PAGE_SIZE, i = 0; size <= maxPage; size <<=1, i++ ){ if( trial == 1 ) access[i] = (double)LONG_MAX; /* perform benchmark access */ access[i] = min( access[i], tlbPageAccess( array, maxMem, 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); } } } selected = getMaxTransition( access, NULL, i ); if( selected < 1 ){ show(VERB_NORMAL," None selected...\n"); return( selected ); } /* We have a valid one, allocate the tlb structure (if needed) for it and annotate the page we selected for it*/ if( type == TYPE_DATA ) tlb=&sys.dataTLB; else tlb=&sys.instTLB; if( *tlb==NULL ){ if( (*tlb = calloc( 1, sizeof(struct tlb))) == NULL ) handleError( "allocating a new tlb structure", EnoMem ); (*tlb)->zOps = *(getZoneOps("tlb")); } (*tlb)->type = type; (*tlb)->pageSize = MIN_TLB_PAGE_SIZE<pageSize ); free( access ); return( 0 ); } /* Run tlb page benchmark for data or instruction tlbs. array - memory to access size - size to access in bytes stride - page size to test type - type of cache to benchmark return - result of benchmark of trial */ static double tlbPageAccess( char *array, unsigned int size, unsigned int stride, type_t type ) { unsigned int sets; double result; /* calculate the number of sets to perform between time calls */ sets = maxVal( (MIN_SET * stride) / size, 1 ); /* perform the appropriate benchmark test */ if( type == TYPE_DATA ) result = dataRead( array, size, stride, sets ); else result = instExecute( array, size, stride, sets ); /* return the average result per operation in ns */ return( result ); } /* We'll return a 1 (valid) if we find that both TLB's are allocated and have a non-zero pageSize value. return - 1 if valid, 0 otherwise */ int tlbPageValid( void ){ if( sys.dataTLB && sys.instTLB && sys.dataTLB->pageSize && sys.instTLB->pageSize ) return 1; /* Assume valid if we have them both with non undefined value */ /* Always has to be run, it's the first test */ return( 0 ); }