benchfn can provided faulty return value
with BMK_extract_returnValue()
This commit is contained in:
+22
-21
@@ -48,9 +48,9 @@
|
|||||||
#define DEBUGOUTPUT(...) { if (DEBUG) DISPLAY(__VA_ARGS__); }
|
#define DEBUGOUTPUT(...) { if (DEBUG) DISPLAY(__VA_ARGS__); }
|
||||||
|
|
||||||
/* error without displaying */
|
/* error without displaying */
|
||||||
#define RETURN_QUIET_ERROR(errorNum, retValue, ...) { \
|
#define RETURN_QUIET_ERROR(retValue, ...) { \
|
||||||
DEBUGOUTPUT("%s: %i: \n", __FILE__, __LINE__); \
|
DEBUGOUTPUT("%s: %i: \n", __FILE__, __LINE__); \
|
||||||
DEBUGOUTPUT("Error %i : ", errorNum); \
|
DEBUGOUTPUT("Error : "); \
|
||||||
DEBUGOUTPUT(__VA_ARGS__); \
|
DEBUGOUTPUT(__VA_ARGS__); \
|
||||||
DEBUGOUTPUT(" \n"); \
|
DEBUGOUTPUT(" \n"); \
|
||||||
return retValue; \
|
return retValue; \
|
||||||
@@ -63,41 +63,48 @@
|
|||||||
|
|
||||||
int BMK_isSuccessful_runOutcome(BMK_runOutcome_t outcome)
|
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 !
|
/* warning : this function will stop program execution if outcome is invalid !
|
||||||
* check outcome validity first, using BMK_isValid_runResult() */
|
* check outcome validity first, using BMK_isValid_runResult() */
|
||||||
BMK_runTime_t BMK_extract_runTime(BMK_runOutcome_t outcome)
|
BMK_runTime_t BMK_extract_runTime(BMK_runOutcome_t outcome)
|
||||||
{
|
{
|
||||||
assert(outcome.tag == 0);
|
assert(outcome.error_tag_never_ever_use_directly == 0);
|
||||||
return outcome.internal_never_use_directly;
|
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;
|
BMK_runOutcome_t b;
|
||||||
memset(&b, 0, sizeof(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;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BMK_runOutcome_t BMK_setValid_runTime(BMK_runTime_t runTime)
|
static BMK_runOutcome_t BMK_setValid_runTime(BMK_runTime_t runTime)
|
||||||
{
|
{
|
||||||
BMK_runOutcome_t outcome;
|
BMK_runOutcome_t outcome;
|
||||||
outcome.tag = 0;
|
outcome.error_tag_never_ever_use_directly = 0;
|
||||||
outcome.internal_never_use_directly = runTime;
|
outcome.internal_never_ever_use_directly = runTime;
|
||||||
return outcome;
|
return outcome;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* initFn will be measured once, benchFn will be measured `nbLoops` times */
|
/* initFn will be measured once, benchFn will be measured `nbLoops` times */
|
||||||
/* initFn is optional, provide NULL if none */
|
/* 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. */
|
/* takes # of blocks and list of size & stuff for each. */
|
||||||
/* can report result of benchFn for each block into blockResult. */
|
/* can report result of benchFn for each block into blockResult. */
|
||||||
/* blockResult is optional, provide NULL if this information is not required */
|
/* 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_runOutcome_t BMK_benchFunction(
|
||||||
BMK_benchFn_t benchFn, void* benchPayload,
|
BMK_benchFn_t benchFn, void* benchPayload,
|
||||||
BMK_initFn_t initFn, void* initPayload,
|
BMK_initFn_t initFn, void* initPayload,
|
||||||
@@ -109,10 +116,7 @@ BMK_runOutcome_t BMK_benchFunction(
|
|||||||
unsigned nbLoops)
|
unsigned nbLoops)
|
||||||
{
|
{
|
||||||
size_t dstSize = 0;
|
size_t dstSize = 0;
|
||||||
|
nbLoops += !nbLoops; /* minimum nbLoops is 1 */
|
||||||
if(!nbLoops) {
|
|
||||||
RETURN_QUIET_ERROR(2, BMK_runOutcome_error(), "nbLoops must be nonzero ");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* init */
|
/* init */
|
||||||
{ size_t i;
|
{ size_t i;
|
||||||
@@ -138,11 +142,8 @@ BMK_runOutcome_t BMK_benchFunction(
|
|||||||
dstBlockBuffers[blockNb], dstBlockCapacities[blockNb],
|
dstBlockBuffers[blockNb], dstBlockCapacities[blockNb],
|
||||||
benchPayload);
|
benchPayload);
|
||||||
if (loopNb == 0) {
|
if (loopNb == 0) {
|
||||||
if (errorFn != NULL)
|
if ((errorFn != NULL) && (errorFn(res))) {
|
||||||
if (errorFn(res)) {
|
RETURN_QUIET_ERROR(BMK_runOutcome_error(res),
|
||||||
BMK_runOutcome_t ro = BMK_runOutcome_error();
|
|
||||||
ro.internal_never_use_directly.sumOfReturn = res;
|
|
||||||
RETURN_QUIET_ERROR(2, ro,
|
|
||||||
"Function benchmark failed on block %u (of size %u) with error %i",
|
"Function benchmark failed on block %u (of size %u) with error %i",
|
||||||
blockNb, (U32)srcBlockBuffers[blockNb], (int)res);
|
blockNb, (U32)srcBlockBuffers[blockNb], (int)res);
|
||||||
}
|
}
|
||||||
@@ -246,7 +247,7 @@ BMK_runOutcome_t BMK_benchTimedFn(
|
|||||||
cont->nbLoops);
|
cont->nbLoops);
|
||||||
|
|
||||||
if(!BMK_isSuccessful_runOutcome(runResult)) { /* error : move out */
|
if(!BMK_isSuccessful_runOutcome(runResult)) { /* error : move out */
|
||||||
return BMK_runOutcome_error();
|
return runResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
{ BMK_runTime_t const newRunTime = BMK_extract_runTime(runResult);
|
{ BMK_runTime_t const newRunTime = BMK_extract_runTime(runResult);
|
||||||
|
|||||||
+51
-43
@@ -26,39 +26,29 @@ extern "C" {
|
|||||||
#include <stddef.h> /* size_t */
|
#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".
|
/* BMK_runTime_t: valid result type */
|
||||||
* 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 ==== */
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned long long nanoSecPerRun; /* time per iteration */
|
unsigned long long nanoSecPerRun; /* time per iteration (over all blocks) */
|
||||||
size_t sumOfReturn; /* sum of return values */
|
size_t sumOfReturn; /* sum of return values */
|
||||||
} BMK_runTime_t;
|
} 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 */
|
/* type expressing the outcome of a benchmark run by BMK_benchFunction().
|
||||||
int BMK_isSuccessful_runOutcome(BMK_runOutcome_t outcome);
|
* benchmark outcome might be valid or invalid.
|
||||||
|
* benchmark outcome can be invalid if and errorFn was provided.
|
||||||
/* extract result from variant type.
|
* BMK_runOutcome_t must be considered "opaque" : never access its members directly.
|
||||||
* note : this function will abort() program execution if result is not valid.
|
* Instead, use its assigned methods :
|
||||||
* check result validity first, by using BMK_isSuccessful_runOutcome()
|
* BMK_isSuccessful_runOutcome, BMK_extract_runTime, BMK_extract_errorResult.
|
||||||
*/
|
* The structure is only described here to allow its allocation on stack. */
|
||||||
BMK_runTime_t BMK_extract_runTime(BMK_runOutcome_t outcome);
|
|
||||||
|
|
||||||
|
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);
|
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)
|
/* benchFn - (*benchFn)(srcBuffers[i], srcSizes[i], dstBuffers[i], dstCapacities[i], benchPayload)
|
||||||
* is run nbLoops times
|
* 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.
|
* This argument can be NULL, in which case nothing is run.
|
||||||
* errorFn - is a function run on each return value of benchFn.
|
* errorFn - is a function run on each return value of benchFn.
|
||||||
* Argument errorFn can be NULL, in which case nothing is run.
|
* 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.
|
* 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
|
* 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
|
* srcBuffers - an array of buffers to be operated on by benchFn
|
||||||
* srcSizes - an array of the sizes of above buffers
|
* srcSizes - an array of the sizes of above buffers
|
||||||
* dstBuffers - an array of buffers to be written into by benchFn
|
* dstBuffers - an array of buffers to be written into by benchFn
|
||||||
* dstCapacities - an array of the capacities of above buffers
|
* 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.
|
* 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.
|
* @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.
|
* Use BMK_isSuccessful_runOutcome() to check if function was successful.
|
||||||
* If yes, extract the result with BMK_extract_runTime(),
|
* If yes, extract the result with BMK_extract_runTime(),
|
||||||
@@ -105,26 +96,30 @@ BMK_runOutcome_t BMK_benchFunction(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* check first if the benchmark was successful or not */
|
||||||
|
int BMK_isSuccessful_runOutcome(BMK_runOutcome_t outcome);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* 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 !
|
||||||
|
*/
|
||||||
|
size_t BMK_extract_errorResult(BMK_runOutcome_t outcome);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* ==== Benchmark any function, returning intermediate results ==== */
|
/* ==== Benchmark any function, returning intermediate results ==== */
|
||||||
|
|
||||||
/* state information tracking benchmark session */
|
/* state information tracking benchmark session */
|
||||||
typedef struct BMK_timedFnState_s BMK_timedFnState_t;
|
typedef struct BMK_timedFnState_s BMK_timedFnState_t;
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
|
|
||||||
/* Tells if duration of all benchmark runs has exceeded total_ms
|
|
||||||
*/
|
|
||||||
int BMK_isCompleted_TimedFn(const BMK_timedFnState_t* timedFnState);
|
|
||||||
|
|
||||||
|
|
||||||
/* BMK_benchTimedFn() :
|
/* BMK_benchTimedFn() :
|
||||||
* Similar to BMK_benchFunction(), most arguments being identical.
|
* Similar to BMK_benchFunction(), most arguments being identical.
|
||||||
* Automatically determines `nbLoops` so that each result is regularly produced at interval of about run_ms.
|
* Automatically determines `nbLoops` so that each result is regularly produced at interval of about run_ms.
|
||||||
@@ -143,6 +138,19 @@ BMK_runOutcome_t BMK_benchTimedFn(
|
|||||||
void *const * dstBlockBuffers, const size_t* dstBlockCapacities,
|
void *const * dstBlockBuffers, const size_t* dstBlockCapacities,
|
||||||
size_t* blockResults);
|
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 */
|
#endif /* BENCH_FN_H_23876 */
|
||||||
|
|||||||
Reference in New Issue
Block a user