lumiera_documentation/src/lib/test/microbenchmark-adaptor.hpp
Ichthyostega a20e233ca0 Library: now using controlled seed and replaced rand (closes #1378)
After augmenting our `lib/random.hpp` abstraction framework to add the necessary flexibility,
a common seeding scheme was ''built into the Test-Runner.''
 * all tests relying on some kind of randomness should invoke `seedRand()`
 * this draws a seed from the `entropyGen` — which is also documented in the log
 * individual tests can now be launched with `--seed` to force a dedicated seed
 * moreover, tests should build a coherent structure of linked generators,
   especially when running concurrently. The existing tests were adapted accordingly

All usages of `rand()` in the code base were investigated and replaced
by suitable calls to our abstraction framework; the code base is thus
isolated from the actual implementation, simplifying further adaptation.
2024-11-17 19:45:41 +01:00

171 lines
5 KiB
C++

/*
MICROBENCHMARK-ADAPTOR.hpp - helper to support microbenchmarks
Copyright (C) Lumiera.org
2023, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/** @file microbenchmark-adaptor.hpp
** Helpers and wrappers so simplify usage of \ref micobenchmark.hpp.
** Notably the benchmark functions expect the actual »test subject« as a
** function or lambda with signature `size_t(size_t)`. The argument will be
** the loop index and the result value will be added into a checksum, which
** also ensures that the optimiser can not unroll the benchmark loop.
** However, in practical use this strict requirement for the signature
** turned out as a nuisance; this header provides some automatic adaption
** - accept any numeric type as argument
** - accept any numeric type as checksum contribution (cast to `size_t`)
** - accept signature `void(void)`
**
*/
#ifndef LIB_TEST_MICROBENCHMARK_ADAPTOR_H
#define LIB_TEST_MICROBENCHMARK_ADAPTOR_H
#include "lib/meta/function.hpp"
#include "lib/meta/util.hpp"
namespace lib {
namespace test{
namespace microbenchmark {
using lib::meta::enable_if;
using std::is_arithmetic;
using std::is_same;
using std::__and_;
using std::__not_;
/**
* @internal helper to expose the signature `size_t(size_t)`
* by wrapping a given lambda or functor.
*/
template<class SIG, typename SEL=void>
struct Adaptor
{
static_assert (not sizeof(SIG), "Unable to adapt given functor.");
};
template<>
struct Adaptor<size_t(size_t)>
{
template<typename FUN>
static decltype(auto)
wrap (FUN&& fun)
{
return std::forward<FUN>(fun);
}
};
template<>
struct Adaptor<void(void)>
{
template<typename FUN>
static auto
wrap (FUN&& fun)
{
return [functor=std::forward<FUN>(fun)]
(size_t) mutable
{
functor();
return size_t(1);
};
}
};
template<typename ON, typename IN>
struct Adaptor<ON(IN), enable_if<__and_<is_arithmetic<IN>, __not_<is_same<IN,size_t>>
,is_arithmetic<ON>, __not_<is_same<ON,size_t>>
>>>
{
template<typename FUN>
static auto
wrap (FUN&& fun)
{
return [functor=std::forward<FUN>(fun)]
(size_t i) mutable
{
return size_t(functor(i));
};
}
};
template<typename ON>
struct Adaptor<ON(void), enable_if<__and_<is_arithmetic<ON>, __not_<is_same<ON,size_t>>
>>>
{
template<typename FUN>
static auto
wrap (FUN&& fun)
{
return [functor=std::forward<FUN>(fun)]
(size_t) mutable
{
return size_t(functor());
};
}
};
template<typename IN>
struct Adaptor<void(IN), enable_if<__and_<is_arithmetic<IN>, __not_<is_same<IN,size_t>>
>>>
{
template<typename FUN>
static auto
wrap (FUN&& fun)
{
return [functor=std::forward<FUN>(fun)]
(size_t i) mutable
{
functor(i);
return size_t(1);
};
}
};
/**
* Adapter to expose the signature `size_t(size_t)`
* from any suitable source functor or lambda
* @note requirements
* - arity must be either zero or one argument
* - if argument or return type are present,
* they must be plain arithmetic types
* - no references allowed
* - can be `void(void)`
*/
template<typename FUN>
inline decltype(auto)
adapted4benchmark (FUN&& fun)
{
static_assert (lib::meta::_Fun<FUN>(), "Need something function-like.");
static_assert (lib::meta::_Fun<FUN>::ARITY <=1, "Function with zero or one argument required.");
using Sig = typename lib::meta::_Fun<FUN>::Sig;
return Adaptor<Sig>::wrap (std::forward<FUN> (fun));
}
}}} // namespace lib::test::microbenchmark
#endif /*LIB_TEST_MICROBENCHMARK_ADAPTOR_H*/