diff --git a/programs/benchfn.c b/programs/benchfn.c index 5ba0c96c29b5ed346d27273e7ea1069e8ea02b05..e2edce14e3fc167b513c8e14fa1f3ee09beb6337 100644 --- a/programs/benchfn.c +++ b/programs/benchfn.c @@ -48,9 +48,9 @@ #define DEBUGOUTPUT(...) { if (DEBUG) DISPLAY(__VA_ARGS__); } /* error without displaying */ -#define RETURN_QUIET_ERROR(errorNum, retValue, ...) { \ +#define RETURN_QUIET_ERROR(retValue, ...) { \ DEBUGOUTPUT("%s: %i: \n", __FILE__, __LINE__); \ - DEBUGOUTPUT("Error %i : ", errorNum); \ + DEBUGOUTPUT("Error : "); \ DEBUGOUTPUT(__VA_ARGS__); \ DEBUGOUTPUT(" \n"); \ return retValue; \ @@ -63,41 +63,48 @@ int BMK_isSuccessful_runOutcome(BMK_runOutcome_t outcome) { - return outcome.tag == 0; + return outcome.error_tag_never_ever_use_directly == 0; } /* warning : this function will stop program execution if outcome is invalid ! * check outcome validity first, using BMK_isValid_runResult() */ BMK_runTime_t BMK_extract_runTime(BMK_runOutcome_t outcome) { - assert(outcome.tag == 0); - return outcome.internal_never_use_directly; + assert(outcome.error_tag_never_ever_use_directly == 0); + return outcome.internal_never_ever_use_directly; } -static BMK_runOutcome_t BMK_runOutcome_error(void) +size_t BMK_extract_errorResult(BMK_runOutcome_t outcome) +{ + assert(outcome.error_tag_never_ever_use_directly != 0); + return outcome.error_result_never_ever_use_directly; +} + +static BMK_runOutcome_t BMK_runOutcome_error(size_t errorResult) { BMK_runOutcome_t b; memset(&b, 0, sizeof(b)); - b.tag = 1; + b.error_tag_never_ever_use_directly = 1; + b.error_result_never_ever_use_directly = errorResult; return b; } static BMK_runOutcome_t BMK_setValid_runTime(BMK_runTime_t runTime) { BMK_runOutcome_t outcome; - outcome.tag = 0; - outcome.internal_never_use_directly = runTime; + outcome.error_tag_never_ever_use_directly = 0; + outcome.internal_never_ever_use_directly = runTime; return outcome; } /* initFn will be measured once, benchFn will be measured `nbLoops` times */ /* initFn is optional, provide NULL if none */ -/* benchFn must return a size_t value compliant with errorFn */ +/* benchFn must return a size_t value that errorFn can interpret */ /* takes # of blocks and list of size & stuff for each. */ /* can report result of benchFn for each block into blockResult. */ /* blockResult is optional, provide NULL if this information is not required */ -/* note : time per loop could be zero if run time < timer resolution */ +/* note : time per loop can be reported as zero if run time < timer resolution */ BMK_runOutcome_t BMK_benchFunction( BMK_benchFn_t benchFn, void* benchPayload, BMK_initFn_t initFn, void* initPayload, @@ -109,10 +116,7 @@ BMK_runOutcome_t BMK_benchFunction( unsigned nbLoops) { size_t dstSize = 0; - - if(!nbLoops) { - RETURN_QUIET_ERROR(2, BMK_runOutcome_error(), "nbLoops must be nonzero "); - } + nbLoops += !nbLoops; /* minimum nbLoops is 1 */ /* init */ { size_t i; @@ -138,11 +142,8 @@ BMK_runOutcome_t BMK_benchFunction( dstBlockBuffers[blockNb], dstBlockCapacities[blockNb], benchPayload); if (loopNb == 0) { - if (errorFn != NULL) - if (errorFn(res)) { - BMK_runOutcome_t ro = BMK_runOutcome_error(); - ro.internal_never_use_directly.sumOfReturn = res; - RETURN_QUIET_ERROR(2, ro, + if ((errorFn != NULL) && (errorFn(res))) { + RETURN_QUIET_ERROR(BMK_runOutcome_error(res), "Function benchmark failed on block %u (of size %u) with error %i", blockNb, (U32)srcBlockBuffers[blockNb], (int)res); } @@ -246,7 +247,7 @@ BMK_runOutcome_t BMK_benchTimedFn( cont->nbLoops); if(!BMK_isSuccessful_runOutcome(runResult)) { /* error : move out */ - return BMK_runOutcome_error(); + return runResult; } { BMK_runTime_t const newRunTime = BMK_extract_runTime(runResult); diff --git a/programs/benchfn.h b/programs/benchfn.h index 3aff676d491c074e1b2b00f6c3a2fe3622212a4c..608ddfa6ee1656a106bf1c845303ca0a179fa793 100644 --- a/programs/benchfn.h +++ b/programs/benchfn.h @@ -26,39 +26,29 @@ extern "C" { #include <stddef.h> /* size_t */ -/* === Variant === */ +/* ==== Benchmark any function, iterated on a set of blocks ==== */ -/* Creates a variant `typeName`, able to express "error or valid result". - * Functions with return type `typeName` - * must first check if result is valid, using BMK_isSuccessful_*(), - * and only then can extract `baseType`. - */ -#define VARIANT_ERROR_RESULT(baseType, variantName) \ - \ -typedef struct { \ - baseType internal_never_use_directly; \ - int tag; \ -} variantName - - -/* ==== Benchmarking any function, iterated on a set of blocks ==== */ +/* BMK_runTime_t: valid result type */ typedef struct { - unsigned long long nanoSecPerRun; /* time per iteration */ - size_t sumOfReturn; /* sum of return values */ + unsigned long long nanoSecPerRun; /* time per iteration (over all blocks) */ + size_t sumOfReturn; /* sum of return values */ } BMK_runTime_t; -VARIANT_ERROR_RESULT(BMK_runTime_t, BMK_runOutcome_t); /* declares BMK_runOutcome_t */ -/* check first if the return structure represents an error or a valid result */ -int BMK_isSuccessful_runOutcome(BMK_runOutcome_t outcome); - -/* extract result from variant type. - * note : this function will abort() program execution if result is not valid. - * check result validity first, by using BMK_isSuccessful_runOutcome() - */ -BMK_runTime_t BMK_extract_runTime(BMK_runOutcome_t outcome); +/* type expressing the outcome of a benchmark run by BMK_benchFunction(). + * benchmark outcome might be valid or invalid. + * benchmark outcome can be invalid if and errorFn was provided. + * BMK_runOutcome_t must be considered "opaque" : never access its members directly. + * Instead, use its assigned methods : + * BMK_isSuccessful_runOutcome, BMK_extract_runTime, BMK_extract_errorResult. + * The structure is only described here to allow its allocation on stack. */ +typedef struct { + BMK_runTime_t internal_never_ever_use_directly; + size_t error_result_never_ever_use_directly; + int error_tag_never_ever_use_directly; +} BMK_runOutcome_t; typedef size_t (*BMK_benchFn_t)(const void* src, size_t srcSize, void* dst, size_t dstCapacity, void* customPayload); @@ -71,19 +61,20 @@ typedef unsigned (*BMK_errorFn_t)(size_t); /* benchFn - (*benchFn)(srcBuffers[i], srcSizes[i], dstBuffers[i], dstCapacities[i], benchPayload) * is run nbLoops times - * initFn - (*initFn)(initPayload) is run once per benchmark, at the beginning. + * initFn - (*initFn)(initPayload) is run once per run, at the beginning. * This argument can be NULL, in which case nothing is run. * errorFn - is a function run on each return value of benchFn. * Argument errorFn can be NULL, in which case nothing is run. * Otherwise, it must return 0 when benchFn was successful, and >= 1 if it detects an error. - * Execution is stopped as soon as an error is detected, and the triggering return value is stored into sumOfReturn. + * Execution is stopped as soon as an error is detected, + * the triggering return value can then be retrieved with BMK_extract_errorResult(). * blockCount - number of blocks. Size of all array parameters : srcBuffers, srcSizes, dstBuffers, dstCapacities, blockResults * srcBuffers - an array of buffers to be operated on by benchFn * srcSizes - an array of the sizes of above buffers * dstBuffers - an array of buffers to be written into by benchFn * dstCapacities - an array of the capacities of above buffers * blockResults - Optional: store the return value of benchFn for each block. Use NULL if this result is not requested. - * nbLoops - defines number of times benchFn is run. + * nbLoops - defines number of times benchFn is run. Minimum value is 1. A 0 is interpreted as a 1. * @return: a variant, which express either an error, or can generate a valid BMK_runTime_t result. * Use BMK_isSuccessful_runOutcome() to check if function was successful. * If yes, extract the result with BMK_extract_runTime(), @@ -105,25 +96,29 @@ BMK_runOutcome_t BMK_benchFunction( -/* ==== Benchmark any function, returning intermediate results ==== */ +/* check first if the benchmark was successful or not */ +int BMK_isSuccessful_runOutcome(BMK_runOutcome_t outcome); -/* state information tracking benchmark session */ -typedef struct BMK_timedFnState_s BMK_timedFnState_t; +/* If the benchmark was successful, extract the result. + * note : this function will abort() program execution if benchmark failed ! + * always check if benchmark was successful first ! + */ +BMK_runTime_t BMK_extract_runTime(BMK_runOutcome_t outcome); -/* BMK_createTimedFnState() and BMK_resetTimedFnState() : - * Create/Set BMK_timedFnState_t for next benchmark session, - * which shall last a minimum of total_ms milliseconds, - * producing intermediate results, paced at interval of (approximately) run_ms. +/* when benchmark failed, it means one invocation of `benchFn` failed. + * The failure was detected by `errorFn`, operating on return value of `benchFn`. + * Returns the faulty return value. + * note : this function will abort() program execution if benchmark did not failed. + * always check if benchmark failed first ! */ -BMK_timedFnState_t* BMK_createTimedFnState(unsigned total_ms, unsigned run_ms); -void BMK_resetTimedFnState(BMK_timedFnState_t* timedFnState, unsigned total_ms, unsigned run_ms); -void BMK_freeTimedFnState(BMK_timedFnState_t* state); +size_t BMK_extract_errorResult(BMK_runOutcome_t outcome); -/* Tells if duration of all benchmark runs has exceeded total_ms - */ -int BMK_isCompleted_TimedFn(const BMK_timedFnState_t* timedFnState); +/* ==== Benchmark any function, returning intermediate results ==== */ + +/* state information tracking benchmark session */ +typedef struct BMK_timedFnState_s BMK_timedFnState_t; /* BMK_benchTimedFn() : * Similar to BMK_benchFunction(), most arguments being identical. @@ -143,6 +138,19 @@ BMK_runOutcome_t BMK_benchTimedFn( void *const * dstBlockBuffers, const size_t* dstBlockCapacities, size_t* blockResults); +/* Tells if duration of all benchmark runs has exceeded total_ms + */ +int BMK_isCompleted_TimedFn(const BMK_timedFnState_t* timedFnState); + +/* BMK_createTimedFnState() and BMK_resetTimedFnState() : + * Create/Set BMK_timedFnState_t for next benchmark session, + * which shall last a minimum of total_ms milliseconds, + * producing intermediate results, paced at interval of (approximately) run_ms. + */ +BMK_timedFnState_t* BMK_createTimedFnState(unsigned total_ms, unsigned run_ms); +void BMK_resetTimedFnState(BMK_timedFnState_t* timedFnState, unsigned total_ms, unsigned run_ms); +void BMK_freeTimedFnState(BMK_timedFnState_t* state); + #endif /* BENCH_FN_H_23876 */