Next: , Up: Advanced Usage   [Contents][Index]


3.1 Comparing Implementations

THe following example shows a more realistic use case in C:

#include <mbenchmark/all.h>
#include <stdio.h>
#include <string.h>

/* Main can be placed anywhere.  */
MICRO_BENCHMARK_MAIN ();

/* Recursive implementation -- overflows with small values!.  */
long
fibrec (int x)
{
  if (x > 1)
    return fibrec (x - 1) + fibrec (x - 2);
  if (x < 0)
    return fibrec (x + 2) - fibrec (x + 1);
  return x;
}

/* Iterative implementation -- overflows with small values!.  */
long
fibit (int x)
{
  long curr, last;
#define impl(c, l, start, sign) \
  last = l; \
  curr = c; \
  for (int i = start; i < sign x; ++i) \
    { \
      long tmp = last sign curr; \
      last = curr; \
      curr = tmp; \
    } \
  return curr

  if (x > 0)
    {
      impl (1, 0, 1, +);
    }
  impl (0, 1, 0, -);
#undef impl
}

/* Prepare the pointer and its value.  */
static int *
set_up (micro_benchmark_test_state s)
{
  static int value = 0;

  value = micro_benchmark_state_get_size (s, 0);
  value *= micro_benchmark_state_get_size (s, 1) ? 1 : -1;
  return &value;
}

static void
tear_down (micro_benchmark_test_state s, int *ptr)
{
  char buf[100];
  const char *name = micro_benchmark_state_get_name (s);
  if (strcmp (name, "test_fibit") == 0)
    snprintf (buf, sizeof (buf) - 1, "fibit/%d", *ptr);
  else
    snprintf (buf, sizeof (buf) - 1, "fibrec/%d", *ptr);
  buf[sizeof (buf) - 1] = '\0';
  micro_benchmark_state_set_name (s, buf);
}

/* Recursive implementation test.  */
static void
test_fibrec (int *r)
{
  long fib = fibrec (*r);
  MICRO_BENCHMARK_DO_NOT_OPTIMIZE (fib);
}

MICRO_BENCHMARK_REGISTER_AUTO_TEST (set_up, test_fibrec, tear_down);

/* Iterative implementation test.  */
static void
test_fibit (int *r)
{
  long fib = fibit (*r);
  MICRO_BENCHMARK_DO_NOT_OPTIMIZE (fib);
}

MICRO_BENCHMARK_REGISTER_AUTO_TEST (set_up, test_fibit, tear_down);

/* Constraints on the recursive test.  */
static void
rec_constraints (micro_benchmark_test_case test)
{
  const size_t sizes[] = { 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 };
  const size_t ssizes = sizeof (sizes) / sizeof (*sizes);
  micro_benchmark_test_case_add_dimension (test, ssizes, sizes);

  const size_t sign[] = { 0, 1 };
  const size_t ssign = sizeof (sign) / sizeof (*sign);
  micro_benchmark_test_case_add_dimension (test, ssign, sign);

  micro_benchmark_clock_time mt = { 3, 0 };
  micro_benchmark_test_case_set_max_time (test, mt);
  micro_benchmark_test_case_limit_iterations (test, 0, 1000000);
}

MICRO_BENCHMARK_CONSTRAINT_TEST ("test_fibrec", rec_constraints);

/* Constraints on the iterative test, this is enough.  */
static void
it_constraints (micro_benchmark_test_case test)
{
  const size_t sizes[] = { 10, 20, 30, 40 };
  const size_t ssizes = sizeof (sizes) / sizeof (*sizes);
  micro_benchmark_test_case_add_dimension (test, ssizes, sizes);

  const size_t sign[] = { 0, 1 };
  const size_t ssign = sizeof (sign) / sizeof (*sign);
  micro_benchmark_test_case_add_dimension (test, ssign, sign);

  micro_benchmark_clock_time mt = { 3, 0 };
  micro_benchmark_test_case_set_max_time (test, mt);
  micro_benchmark_test_case_limit_iterations (test, 0, 1000000);
}

MICRO_BENCHMARK_CONSTRAINT_TEST ("test_fibit", it_constraints);

This is its C++ version:

#include <mbenchmark/all.hpp>
#include <functional> /*  std::plus and std::less  */
#include <string>

/* Main can be placed anywhere.  */
MICRO_BENCHMARK_MAIN ();

namespace
{
  /* Recursive implementation.  */
  template <typename T, typename I = int>
  T
  fibrec (I x)
  {
    if (x > 1)
      return fibrec<T> (x - 1) + fibrec<T> (x - 2);
    if (x < 0)
      return fibrec<T> (x + 2) - fibrec<T> (x + 1);
    return x;
  }

  /* Iterative implementation.  */
  template <typename T, typename I = int>
  T
  fibit (I x)
  {
    auto doit = [x] (T curr, T last, I start, auto&& op)
    {
      for (I i = start; i < op (0, x); ++i)
        {
          T tmp = op (last, curr);
          last = curr;
          curr = tmp;
        }
      return curr;
    };
    if (x > 0)
      return doit (T{1}, T{0}, 1, std::plus<T>{});
    return doit (T{0}, T{1}, 0, std::minus<T>{});
  }

  /* Prepare the pointer and its value.  */
  int
  set_up (micro_benchmark::state const& s)
  {
    auto sizes = s.sizes ();
    int value = sizes.at (0);
    value *= sizes.at (1) ? 1 : -1;
    return value;
  }

  void
  tear_down (micro_benchmark::state& s, int v)
  {
    constexpr auto fibit_base = "fibit/";
    constexpr auto fibrec_base = "fibrec/";
    std::string name = s.get_name ();
    if (name == "test_fibit")
      s.set_name (fibit_base + std::to_string (v));
    else
      s.set_name (fibrec_base + std::to_string (v));
  }

  /* Constraints on the recursive test.  */
  void
  rec_constraints (micro_benchmark::test_case& test)
  {
    test.add_dimension ({ 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30 });
    test.add_dimension ({ 0, 1 });

    test.limit_iterations (0, 1000000);
  }

  /* Register the recursive test with its constraints.  */
  void
  rtestfun (int v)
  {
    auto r = fibrec<long> (v);
    micro_benchmark::do_not_optimize (r);
  }

  auto rtest =
    micro_benchmark::register_test (micro_benchmark::with_constraints,
                                    "test_fibrec", rec_constraints,
                                    rtestfun, set_up, tear_down);

  /* Constraints on the iterative test, this is enough.  */
  void
  it_constraints (micro_benchmark::test_case& test)
  {
    test.add_dimension ({ 10, 20, 30, 40 });
    test.add_dimension ({ 0, 1 });

    test.limit_iterations (0, 1000000);
  }

  /* Register the iterative test with its constraints.  */
  void
  itestfun (int v)
  {
    auto r = fibit<long> (v);
    micro_benchmark::do_not_optimize (r);
  }

  auto itest =
    micro_benchmark::register_test (micro_benchmark::with_constraints,
                                    "test_fibit", it_constraints,
                                    itestfun, set_up, tear_down);
}

This is its Guile version:

(define (fibrec n)
  (cond ((> n 1) (+ (fibrec (- n 2)) (fibrec (- n 1))))
        ((< n 0) (- (fibrec (+ n 2)) (fibrec (+ n 1))))
        (else n)))

(define (fibit n)
  (define (up curr last p)
    (if (> p 0)
        (up (+ curr last) curr (- p 1))
        curr))
  (define (down curr last p)
    (if (< p 0)
        (down (- last curr) curr (+ p 1))
        curr))
  (cond ((> n 1) (up 1 0 (- n 1)))
        ((< n 0) (down 0 1 n))
        (else n)))

(define (set-up state)
  (define (doit size sign)
    (if (eqv? sign 0)
	(list size)
	(list (- size))))
  (apply doit (state-sizes state)))

(define (tear-down state n)
  (let ((base (if (equal? (state-name state) "fibit") "fibit/" "fibrec/")))
    (set-state-name! state (string-append base (number->string n)))))

(register-test! "fibit"
                #:test fibit
                #:set-up set-up
                #:tear-down tear-down
                #:dimensions '((10 50 100 500 1000 5000 10000 50000 100000)
                               (0 1))
                #:max-iterations 1000000)
(register-test! "fibrec"
                #:test fibrec
                #:set-up set-up
                #:tear-down tear-down
                #:dimensions '((10 12 14 16 18 20 22 24 26 28 30)
                               (0 1))
                #:max-iterations 1000000)

(main (command-line))

These file can be found on the source code tree as doc/examples/fib.c, doc/examples/fib.cxx and doc/examples.fib.scm respectively.

Their output would be something like this:

fib --brief --log-level=warn
Suite: fib (40 test executions)
========================================
    Test Name | Iterations | It.Time (μ)
========================================
        fibit |         -- |          --
     fibit/10 |    1000000 |       252ns
     fibit/50 |    1000000 |       429ns
    fibit/100 |    1000000 |      1.64μs
    fibit/500 |      92253 |      53.3μs
   fibit/1000 |      32308 |       159μs
   fibit/5000 |       2509 |      2.00ms
  fibit/10000 |        639 |      7.84ms
  fibit/50000 |         32 |       157ms
 fibit/100000 |          9 |       582ms
    fibit/-10 |    1000000 |       308ns
    fibit/-50 |    1000000 |       503ns
   fibit/-100 |    1000000 |      1.94μs
   fibit/-500 |      80266 |      62.7μs
  fibit/-1000 |      29633 |       169μs
  fibit/-5000 |       2153 |      2.33ms
 fibit/-10000 |        576 |      8.78ms
 fibit/-50000 |         29 |       173ms
fibit/-100000 |          9 |       621ms
----------------------------------------
       fibrec |         -- |          --
    fibrec/10 |    1000000 |      2.07μs
    fibrec/12 |     884087 |      4.53μs
    fibrec/14 |     388238 |      11.7μs
    fibrec/16 |     155112 |      31.1μs
    fibrec/18 |      60585 |      81.3μs
    fibrec/20 |      23411 |       213μs
    fibrec/22 |       8991 |       555μs
    fibrec/24 |       3441 |      1.45ms
    fibrec/26 |       1316 |      3.80ms
    fibrec/28 |        510 |      9.84ms
    fibrec/30 |        195 |      25.7ms
   fibrec/-10 |    1000000 |      2.81μs
   fibrec/-12 |     593652 |      7.29μs
   fibrec/-14 |     243955 |      19.3μs
   fibrec/-16 |      95423 |      51.0μs
   fibrec/-18 |      36732 |       135μs
   fibrec/-20 |      13949 |       357μs
   fibrec/-22 |       5257 |     0.945ms
   fibrec/-24 |       1998 |      2.51ms
   fibrec/-26 |        770 |      6.53ms
   fibrec/-28 |        295 |      17.0ms
   fibrec/-30 |        113 |      44.5ms
----------------------------------------
========================================

The recursive implementation speed decreases very quickly when the magnitude increases. On the other hand, the iterative version can calculate several times values with the same order of magnitude.


Next: Report Output, Up: Advanced Usage   [Contents][Index]