This is something I wanted to clean-up since a very long time indeed, but always postponed it, since it requires a lot of nasty clean-up and disentangling.... Several parts had been stashed away into `src/lib/test`, which are not really related: * the testrunner and testsuite builder * generic helper utilities * specialised test-support facilities, e.g. the EventLog This changeset introduces a new top-level test folder and a namespace `::test` Code from this folder is linked into a shared library `libtestsupport.so`, which resides in the linkage hierarchy between »Vessel« and »Vault«, yet is no longer part of the Lumiera application and only linked into the test-suite.
161 lines
4.6 KiB
C++
161 lines
4.6 KiB
C++
/*
|
||
MICROBENCHMARK-ADAPTOR.hpp - helper to support microbenchmarks
|
||
|
||
Copyright (C)
|
||
2023, Hermann Vosseler <Ichthyostega@web.de>
|
||
|
||
**Lumiera** 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. See the file COPYING for further details.
|
||
|
||
*/
|
||
|
||
|
||
/** @file microbenchmark-adaptor.hpp
|
||
** Helpers and wrappers so simplify usage of \ref microbenchmark.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 TEST_MICROBENCHMARK_ADAPTOR_H
|
||
#define TEST_MICROBENCHMARK_ADAPTOR_H
|
||
|
||
|
||
#include "lib/meta/function.hpp"
|
||
#include "lib/meta/util.hpp"
|
||
|
||
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 = lib::meta::_Fun<FUN>::Sig;
|
||
|
||
return Adaptor<Sig>::wrap (std::forward<FUN> (fun));
|
||
}
|
||
|
||
|
||
|
||
}} // namespace test::microbenchmark
|
||
#endif /*TEST_MICROBENCHMARK_ADAPTOR_H*/
|