Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BENCH_INNER : lmbench3.0 src code macro query

I was reading the MHZ - Anatomy of a Benchmark paper by the creators of lmbench and source browsing the code alongside.

  1. The paper can be downloaded @ MHz : Anatomy of a Microbenchmark
  2. Source Code lmbench-3.0 authored by Carl Staelin and Larry McVoy

Inside the BENCH_INNER() macro I have a doubt :

#define BENCH_INNER(loop_body, enough) {                \
    static iter_t   __iterations = 1;               \
    int     __enough = get_enough(enough);          \
    iter_t      __n;                        \
    double      __result = 0.;                  \
                                    \
    while(__result < 0.95 * __enough) {             \
        start(0);                       \
        for (__n = __iterations; __n > 0; __n--) {      \
            loop_body;                  \
        }                           \
        __result = stop(0,0);                   \
        if (__result < 0.99 * __enough              \
            || __result > 1.2 * __enough) {         \
            if (__result > 150.) {              \
                double  tmp = __iterations / __result;  \
                tmp *= 1.1 * __enough;          \
                __iterations = (iter_t)(tmp + 1);   \
            } else {                    \
                if (__iterations > (iter_t)1<<27) { \
                    __result = 0.;          \
                    break;              \
                }                   \
                __iterations <<= 3;         \
            }                       \
        }                           \
    } /* while */                           \
    save_n((uint64)__iterations); settime((uint64)__result);    \
}
  1. From what I understood, BENCH_INNER is used to auto compute the optimal number of iterations for a chosen timing interval ('enough'). The loop executes till we keep iterating over a piece of code 'loop_body' that would take at least 95% of our chosen timing interval which could range from 5ms to a 1second.

  2. For simplicity sake let us take 'enough' to be 10000 microseconds

  3. We start off with __iterations = 1
  4. Say over time we reached a stage where __result > 1.2 * 'enough' i.e. __result > 12000 microseconds
  5. Now since __result > 150 microseconds, we go ahead and scale the value of __iterations so that __result would be approximately equal to 1.1 * 'enough'
  6. But before we can recompute __result, we would break the loop since the earlier __result > .95 * 'enough'
  7. We go ahead and save the value of __result and modified value __iterations (here the value of __result is not for the __iterations which we save)

Shouldn't the code in such a case recompute __result ? Did I miss something fundamental ?

like image 842
Anoop Menon Avatar asked Mar 24 '26 07:03

Anoop Menon


1 Answers

Yes there is a problem here, __result must be set to zero.

And I can see one more possible problem in your code -- result is comparing with0.99*enough in one case and with0.95*enough in other case, that is highly suspicious to be a typo. I suggest you to rewrite this macro, explicitly stating "satisfying" conditions and to simplify logic, checking good conditions first. Like this:

#define SEARCH_EXIT_CASE(__result, __enough) ((__result) > 0.95 * (__enough) && (__result) < 1.2 * (__enough))

#define BENCH_INNER(loop_body, enough) {                \
    static iter_t   __iterations = 1;               \
    int     __enough = get_enough(enough);          \
    iter_t      __n;                        \
    double      __result = 0.;                  \
                                    \
    while(!SEARCH_EXIT_CASE(__result, __enough)) {             \
        start(0);                       \
        for (__n = __iterations; __n > 0; __n--) {      \
            loop_body;                  \
        }                           \
        __result = stop(0,0);                   \
        /* good result */ \
        if (SEARCH_EXIT_CASE(__result, __enough)) {         \
            break; \
        } \
        /* failure cases */ \
        if (__result > 150.) {              \
            double  tmp = __iterations / __result;  \
            tmp *= 1.1 * __enough;          \
            __iterations = (iter_t)(tmp + 1);   \
        } else { \
            if (__iterations > (iter_t)1<<27) { \
                __result = 0.;          \
                break;              \
            }                   \
            __iterations <<= 3;         \
        } \
        __result = 0.;          \
    } /* while */                           \
    save_n((uint64)__iterations); settime((uint64)__result);    \
}

Also I suggest to define other magical constants like 1<<27, 1.1, 3, 150.0 to have meaningful names likeMAX_ITER, CORRECTION_RATE,INCREASE_RATE,RESULT_OVERFLOW, etc...

like image 136
Konstantin Vladimirov Avatar answered Mar 26 '26 22:03

Konstantin Vladimirov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!