From c3705474618c00a2b091a509c687582da2c1c378 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Wed, 18 May 2016 11:50:36 +0200 Subject: [PATCH] Use ns.bench + Add bitwise_and example --- CMakeLists.txt | 38 +- bench/CMakeLists.txt | 13 + bench/function/CMakeLists.txt | 9 + bench/function/simd/CMakeLists.txt | 13 + bench/function/simd/bitwise_and.cpp | 100 ++ bench/ns.bench.hpp | 1447 +++++++++++++++++++++++++++ test/setup.cmake => setup.cmake | 14 +- test/CMakeLists.txt | 1 - 8 files changed, 1610 insertions(+), 25 deletions(-) create mode 100644 bench/CMakeLists.txt create mode 100644 bench/function/CMakeLists.txt create mode 100644 bench/function/simd/CMakeLists.txt create mode 100644 bench/function/simd/bitwise_and.cpp create mode 100644 bench/ns.bench.hpp rename test/setup.cmake => setup.cmake (87%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 44e18cb88..6e818663f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,27 +45,26 @@ NS_prevent_in_source_build() ## External projects ## ------------------------------------------------------------------------------------------------- -set(STF_STANDALONE_DESTINATION ${PROJECT_SOURCE_DIR}/test) -set(BRIGAND_STANDALONE_DESTINATION ${PROJECT_SOURCE_DIR}/include/boost/simd/detail) - -NS_project_include(brigand.standalone) +set(STF_STANDALONE_DESTINATION ${PROJECT_SOURCE_DIR}/test) +set(BRIGAND_STANDALONE_DESTINATION ${PROJECT_SOURCE_DIR}/include/boost/simd/detail) +set(NS_BENCH_STANDALONE_DESTINATION ${PROJECT_SOURCE_DIR}/bench) +set(NS_CMAKE_PROJECT_OPTIONS + CMAKE_ARGS "-DBOOST_ROOT=${BOOST_HEADER_ONLY_DESTINATION}/include" +) if (DEFINED USE_SELF_BOOST) set(BOOST_HEADER_ONLY_GIT_TAG develop) NS_project_include(boost-header-only) + set(Boost_INCLUDE_DIRS ${BOOST_HEADER_ONLY_DESTINATION}/include) +endif() - # NOTE: (workaround) - # We DO need to use `CMAKE_ARGS` here otherwise, travis is not able to find boost even when - # exporting BOOST_ROOT using shell export... - set(BOOST_DISPATCH_GIT_TAG develop) - set(BOOST_DISPATCH_OPTIONS - CMAKE_ARGS "-DBOOST_ROOT=${BOOST_HEADER_ONLY_DESTINATION}/include" - ) - - set( STF_OPTIONS - CMAKE_ARGS "-DBOOST_ROOT=${BOOST_HEADER_ONLY_DESTINATION}/include" - ) +# Use the same BOOST_ROOT for every projects +set(NS_CMAKE_PROJECT_OPTIONS + CMAKE_ARGS "-DBOOST_ROOT=${Boost_INCLUDE_DIRS}" +) +if (DEFINED USE_SELF_BOOST) + set(BOOST_DISPATCH_GIT_TAG develop) NS_project_include(boost.dispatch) add_dependencies(BOOST_DISPATCH BOOST_HEADER_ONLY) add_dependencies(BOOST_DISPATCH-install BOOST_HEADER_ONLY) @@ -76,6 +75,8 @@ if (DEFINED USE_SELF_BOOST) ) endif() +NS_project_include(ns.bench.standalone) +NS_project_include(brigand.standalone) NS_project_include(stf.standalone) ## Compute version string and mode + Documentation @@ -97,17 +98,22 @@ endif() include_directories( ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/test + ${PROJECT_SOURCE_DIR}/bench ${Boost_INCLUDE_DIRS} ) -## Setup Unit & Coverage Test +## Setup Unit/Bench & Coverage Test ## ------------------------------------------------------------------------------------------------- +include(./setup.cmake) # Import compiler specific setup include(CTest) add_custom_target(tests) add_custom_target(unit) add_dependencies(tests unit) add_subdirectory(test) +add_custom_target(bench) +add_subdirectory(bench) + if (NOT DEFINED IS_TRAVIS_CI) NS_include(coverage) enable_coverage(boost.simd) diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt new file mode 100644 index 000000000..eafc1dd83 --- /dev/null +++ b/bench/CMakeLists.txt @@ -0,0 +1,13 @@ +## ------------------------------------------------------------------------------------------------- +## Copyright 2016 - NumScale SAS +## +## Distributed under the Boost Software License, Version 1.0. +## See accompanying file LICENSE.txt or copy at +## http://www.boost.org/LICENSE_1_0.txt +## ------------------------------------------------------------------------------------------------- + +NS_include(make_bench) + +set(CMAKE_BUILD_TYPE "Release") + +add_subdirectory(function) diff --git a/bench/function/CMakeLists.txt b/bench/function/CMakeLists.txt new file mode 100644 index 000000000..63153d7e2 --- /dev/null +++ b/bench/function/CMakeLists.txt @@ -0,0 +1,9 @@ +## ------------------------------------------------------------------------------------------------- +## Copyright 2016 - NumScale SAS +## +## Distributed under the Boost Software License, Version 1.0. +## See accompanying file LICENSE.txt or copy at +## http://www.boost.org/LICENSE_1_0.txt +## ------------------------------------------------------------------------------------------------- + +add_subdirectory(simd) diff --git a/bench/function/simd/CMakeLists.txt b/bench/function/simd/CMakeLists.txt new file mode 100644 index 000000000..c1a9ac917 --- /dev/null +++ b/bench/function/simd/CMakeLists.txt @@ -0,0 +1,13 @@ +## ------------------------------------------------------------------------------------------------- +## Copyright 2016 - NumScale SAS +## +## Distributed under the Boost Software License, Version 1.0. +## See accompanying file LICENSE.txt or copy at +## http://www.boost.org/LICENSE_1_0.txt +## ------------------------------------------------------------------------------------------------- + +set(SOURCES + bitwise_and.cpp +) + +make_bench("function.simd" ${SOURCES}) diff --git a/bench/function/simd/bitwise_and.cpp b/bench/function/simd/bitwise_and.cpp new file mode 100644 index 000000000..45e045b8b --- /dev/null +++ b/bench/function/simd/bitwise_and.cpp @@ -0,0 +1,100 @@ +// ------------------------------------------------------------------------------------------------- +// Copyright 2016 - NumScale SAS +// +// Distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt +// ------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +namespace bs = boost::simd; +namespace nsb = ns::bench; + +template inline std::string type_id() +{ + typedef std::is_const::type> const_t; + typedef std::is_lvalue_reference lref_t; + typedef std::is_rvalue_reference rref_t; + + std::string s = boost::core::demangle(typeid(T).name()); + s += const_t::value ? " const" : ""; + s += lref_t::value ? "&" : ""; + s += rref_t::value ? "&&" : ""; + + return s; +} + +/// @overload +template inline std::string type_id( const T& ) +{ + return type_id(); +} + + +template +struct bitwise_and_packed +{ + template + void operator()(U min1, U max1) + { + using pack_t = bs::pack; + nsb::make_function_experiment_cpe_sized_ + ( [](const pack_t& x, const pack_t& y ) -> pack_t + { + //std::cout << type_id() << std::endl; + return bs::bitwise_and(x, y); + } + , nsb::generators::rand(min1, max1) + , nsb::generators::rand(min1, max1) + ); + } +}; + +template +struct bitwise_and_scalar +{ + template + void operator()(U min1, U max1) + { + nsb::make_function_experiment_cpe_sized_<1> + ( [](const T & x, const T & y) -> T + { return bs::bitwise_and(x, y); } + , nsb::generators::rand(min1, max1) + , nsb::generators::rand(min1, max1) + ); + } +}; + +template +struct bitwise_and_std +{ + template + void operator()(U min1, U max1) + { + nsb::make_function_experiment_cpe_sized_<1> + ( [](const T & x, const T & y) -> T + { return x & y; } + , nsb::generators::rand(min1, max1) + , nsb::generators::rand(min1, max1) + ); + } +}; + +int main(int argc, char **argv) { + nsb::parse_args(argc, argv); + + nsb::make_for_each(-10, 10); + //nsb::make_for_each(-10, 10); + + //nsb::make_for_each(0, 20); + //nsb::make_for_each(-10, 10); + //nsb::make_for_each(-10, 10); + //nsb::make_for_each(-10, 10); + return 0; +} + diff --git a/bench/ns.bench.hpp b/bench/ns.bench.hpp new file mode 100644 index 000000000..a988e7b91 --- /dev/null +++ b/bench/ns.bench.hpp @@ -0,0 +1,1447 @@ +// ------------------------------------------------------------------------------------------------- +/*! + @file Main header for benchmark components + @copyright 2016 - NumScale SAS + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) +**/ +// ------------------------------------------------------------------------------------------------- +#ifndef NS_BENCH_HPP_INCLUDED +#define NS_BENCH_HPP_INCLUDED +namespace ns { namespace bench { +/*! + @defgroup group-bench Benchmark components + Benchmark components + **/ +} } +#include +#include +namespace ns { +template inline std::string type_id() +{ + typedef std::is_const::type> const_t; + typedef std::is_lvalue_reference lref_t; + typedef std::is_rvalue_reference rref_t; + std::string s = boost::core::demangle(typeid(T).name()); + s += const_t::value ? " const" : ""; + s += lref_t::value ? "&" : ""; + s += rref_t::value ? "&&" : ""; + return s; +} +template inline std::string type_id( const T& ) +{ + return type_id(); +} +} +#include +#include +#include +namespace ns { namespace args { +struct parser; +namespace detail { + struct option; + template struct typed_option; +} +struct options_map { + using key_type = std::string; + using value_type = std::string; + using options_container = std::unordered_map; + using anonymous_container = std::list; + bool has(key_type const& opt_name) const { + return opts_map_.find(opt_name) != opts_map_.end(); + } + template + T get(key_type const& opt_name) const { + return boost::lexical_cast(opts_map_.at(opt_name)); + } + template + T get_or(key_type const& opt_name, T const& fallback) const { + return has(opt_name) ? get(opt_name) : fallback; + } + anonymous_container const& anonymous() const { + return anon_map_; + } + options_container const& options() const { + return opts_map_; + } + private: + void add_option(key_type const& name, value_type const& value) { + opts_map_[name] = value; + } + void add_anonymous(value_type const& value) { + anon_map_.emplace_back(value); + } + friend parser; + friend detail::option; + template friend struct detail::typed_option; + private: + options_container opts_map_; + anonymous_container anon_map_; +}; +} } +#include +namespace ns { namespace args { +struct parse_error : std::runtime_error { + parse_error(std::string const& what) + : std::runtime_error(what) { + } +}; +} } +#include +#include +namespace ns { namespace args { namespace detail { +struct option { + option(std::string name, std::string opts, bool required) + : name_(name), opts_(opts), required_(required) { + } + virtual bool validate(options_map& m, std::string const& arg, std::string const& opt) const { + if (arg.find(opt) == 0 && opt.length() == arg.length()) { + m.add_option(name(), ""); + return true; + } + return false; + } + virtual bool need_value() const { + return false; + } + bool parse(options_map& m, std::string arg) const { + std::string opts(opts_); + boost::char_separator sep(","); + for (auto opt : boost::tokenizer(opts, sep)) { + if (validate(m, arg, opt)) { + return true; + } + } + return false; + } + std::string usage() const { + std::string u; + std::string s; + std::size_t how_many = 0; + boost::char_separator sep(","); + for (auto opt : boost::tokenizer(std::string(opts()), sep)) { + if (how_many) { + s += " | " + opt; + } else { + s += opt; + how_many += 1; + } + if (need_value()) { + s += "="; + } + } + if (!required()) { + u += "["; + u += s; + u += "]"; + } else if (how_many) { + u += "("; + u += s; + u += ")"; + } else { + u = s; + } + return u; + } + std::string const& name() const { return name_; } + std::string const& opts() const { return opts_; } + bool required() const { return required_; } + private: + std::string name_; + std::string opts_; + bool required_; +}; +template +struct typed_option : option { + typed_option(std::string name, std::string opts, bool required) + : option(name, opts, required) { + } + virtual bool validate(options_map& m, std::string const& arg, std::string const& opt) const { + auto pos = arg.find(opt); + if (pos == 0) { + if (arg[opt.length()] == '=') { + auto rhs = arg.substr(opt.length() + 1, arg.length() - opt.length() - 1); + try { + (void)boost::lexical_cast(rhs); + } catch (boost::bad_lexical_cast) { + throw parse_error(opts() + " expects a valid value of type: " + ns::type_id()); + } + m.add_option(name(), rhs); + return true; + } + } + return false; + } + virtual bool need_value() const { + return true; + } +}; +template +struct auto_option { + using type = typed_option; +}; +template +struct auto_option::value>::type> { + using type = option; +}; +} } } +#include +#include +#include +namespace ns { namespace args { +struct parser { + public: + parser(std::string const& bin_name = "") + : bin_name_(bin_name), opts_() { + } + template + void add_option(std::string const& name, std::string const& opts, bool required = false) { + opts_.emplace(name, new typename detail::auto_option::type(name, opts, required)); + } + std::string usage() const { + std::string u = "usage: "; + u += bin_name_; + u += " [-h | --help] "; + bool first = true; + for (auto opt_pair : opts_) { + if (!first) { + u += " " + opt_pair.second->usage(); + } else { + u += opt_pair.second->usage(); + first = false; + } + } + u += "\n"; + return u; + } + void parse(options_map& m, int argc, char **argv) const { + try { + parse_(m, argc, argv); + } catch (parse_error const& e) { + std::string s; + s += usage(); + s += bin_name_; + s += ": "; + s += "error: "; + s += e.what(); + s += "\n"; + throw parse_error(s); + } + } + void parse(options_map& m, int argc, char **argv, std::nothrow_t const&) const { + try { + parse(m, argc, argv); + } catch (parse_error const& e) { + std::cerr << e.what(); + std::cerr.flush(); + std::exit(1); + } + } + private: + void parse_(options_map& m, int argc, char **argv) const { + for (int i = 1; i < argc; ++i) { + std::string arg(argv[i]); + if (arg == "-h" || arg == "--help") { + std::cout << usage(); + std::cout.flush(); + std::exit(0); + } + bool parsed = false; + for (auto opt_pair : opts_) { + auto opt = opt_pair.second; + if (opt->parse(m, arg)) { + parsed = true; + break; + } + } + if (!parsed) { + if (arg[0] == '-') { + throw parse_error(arg + " unknown predicate!"); + } + m.add_anonymous(arg); + } + } + for (auto opt_pair : opts_) { + auto opt = opt_pair.second; + if (opt->required() && !m.has(opt->name())) { + throw parse_error(opt->usage() + " is required!"); + } + } + } + private: + const std::string bin_name_; + std::unordered_map opts_; +}; +} } +std::ostream& operator<<(std::ostream& os, ns::args::options_map const& m) { + os << "anonymous( "; + for (auto a : m.anonymous()) { + os << a << ", "; + } + os << ")" << std::endl;; + os << "options{ "; + for (auto a : m.options()) { + os << a.first << ":" << a.second << ", "; + } + os << "}" << std::endl;; + return os; +} +std::ostream& operator<<(std::ostream& os, ns::args::parser const& p) { + os << p.usage(); + os.flush(); + return os; +} +#include +#include +namespace ns { namespace bench { +ns::args::options_map args_map; +inline void parse_args(int argc, char **argv) { + ns::args::parser p(argv[0]); + p.add_option("frequency", "-f,--frequency"); + p.add_option("duration" , "-d,--duration" ); + p.add_option("iteration", "-i,--iteration"); + p.parse(args_map, argc, argv, std::nothrow); +} +} } +#include +#include +#include +#include +#include +#if BOOST_COMP_MSVC + #include +#endif +namespace ns { namespace bench { + using cycle_t = std::uint64_t; + BOOST_FORCEINLINE cycle_t read_cycles() { + cycle_t c = 0; +#if (BOOST_COMP_GNUC || BOOST_COMP_CLANG || BOOST_COMP_INTEL) && BOOST_ARCH_X86 + std::uint32_t hi = 0, lo = 0; + __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi) : : "memory" ); + c = static_cast(lo) | (static_cast(hi) << 32); +#elif BOOST_COMP_MSVC + #if defined(_M_AMD64) + __faststorefence(); + #elif defined(_M_IX86) + long cpu_barrier; __asm xchg cpu_barrier, eax; + #endif + ::_ReadWriteBarrier(); + c = ( ::__rdtsc() ); + ::_ReadWriteBarrier(); +#elif (BOOST_COMP_GNUC && BOOST_ARCH_PPC) \ + || (BOOST_COMP_MWERKS && BOOST_OS_MACOS) \ + || (defined(__IBM_GCC_ASM) && BOOST_ARCH_PPC) + std::uint32_t tbl, tbu0, tbu1; + do + { + __asm__ __volatile__ ("mftbu %0" : "=r"(tbu0)); + __asm__ __volatile__ ("mftb %0" : "=r"(tbl)); + __asm__ __volatile__ ("mftbu %0" : "=r"(tbu1)); + } while (tbu0 != tbu1); + c = (cycle_t)((((std::uint64_t)tbu0) << 32) | tbl); +#elif BOOST_OS_LINUX && !defined(BOOST_OS_ANDROID_AVAILABLE) +#else + static_assert(false, "read_cycles not available"); +#endif + return c; + } +} } +#include +#include +#include +#include +#include +#include +namespace ns { namespace bench { +template +struct basic_cycle_clock { + using rep = cycle_t; + using period = std::ratio<1, Freq>; + using duration = std::chrono::duration; + using time_point = std::chrono::time_point; + const bool is_steady = false; + static time_point now() { + return time_point(duration(read_cycles())); + } +}; +#ifndef NS_BENCH_FREQUENCY + #define NS_BENCH_FREQUENCY 1 +#endif +using cycle_clock = basic_cycle_clock; +} } +#include +#include +#include +#include +#include +#include +#include +#include +namespace ns { namespace bench { + using accumulators = boost::accumulators::stats< boost::accumulators::tag::mean + , boost::accumulators::tag::median + , boost::accumulators::tag::min + , boost::accumulators::tag::max + , boost::accumulators::tag::count + >; + using stats_set = boost::accumulators::accumulator_set; + thread_local stats_set times_set, cycles_set; +} } +namespace ns { namespace bench { namespace stats { +struct basic_stat { + virtual double eval(stats_set const&) const = 0; + virtual std::string unit() const = 0; +}; +} } } +#include +#include +namespace ns { namespace bench { namespace format { +static const int unit_maxw = 10; +static const int stat_maxw = 10; +static const int size_maxw = 10; +static const int result_maxw = 15; +static const int samples_maxw = 15; +static const int parameters_maxw = 80; +static std::reference_wrapper global_ostream = std::ref(std::cout); +inline std::ostream& get_os() { + return global_ostream.get(); +} +inline void set_os(std::ostream& os) { + global_ostream = std::ref(os); +} +inline std::string format_line(std::string const& what) { + return what.empty() ? what : std::string(":: ") + what + "\n"; +} +inline void print_line(std::string const& what) { + get_os() << format_line(what); +} +inline std::size_t print_bar(std::size_t size, char what) { + auto& os = get_os(); + for (std::size_t i = 0; i < size; ++i) { + os << what; + } + os << std::endl; + return size; +} +inline std::size_t print_header() { + std::stringstream ss; + ss + << std::setw(format::size_maxw) + << "Size" + << ' ' + << std::setw(format::result_maxw) + << "Result" + << ' ' + << std::setw(format::unit_maxw) + << "Unit" + << ' ' + << std::setw(format::stat_maxw) + << "Stat" + << ' ' + << std::setw(format::samples_maxw) + << "Samples" + << ' ' + << std::setw(format::parameters_maxw) + << "Parameters" + << ' ' + << std::endl; + ; + get_os() << ss.str(); + return ss.str().size(); +} +inline std::size_t print_experiment(std::string const& benchmark) { + get_os() << format_line(std::string("Benchmarking: \"") + benchmark +"\""); + return print_bar(print_header(), '-'); +} +template +inline void print_results(Experiment const& e, Metric const& m, double res) { + std::size_t number_of_samples = boost::accumulators::count(times_set); + auto& os = get_os(); + std::ios::fmtflags saved_fmt(os.flags()); + os + << std::setw(format::size_maxw) + << e.size() + << ' ' + << std::setw(format::result_maxw) + << res + << ' ' + << std::setw(format::unit_maxw) + << m.unit().unit() + << ' ' + << std::setw(format::stat_maxw) + << m.stat().unit() + << ' ' + << std::setw(format::samples_maxw) + << number_of_samples + << ' ' + << std::setw(format::parameters_maxw) + << e.description() + << std::endl + ; + os.flags(saved_fmt); +} +struct scoped_printer { + scoped_printer(std::string const& name) + : bar_size_(0) + { + bar_size_ = print_experiment(name); + } + ~scoped_printer() + { + print_bar(bar_size_, '='); + } + private: + std::size_t bar_size_; +}; +template +T sanitize(T const& v) { return v; } +inline int sanitize(char v) { return v; } +inline int sanitize(std::int8_t v) { return v; } +inline int sanitize(std::uint8_t v) { return v; } +} } } +#include +#include +#include +#include +#include +namespace ns { namespace bench { +struct basic_experiment { + virtual void before_run() { + } + virtual void after_run() { + } + virtual std::size_t size() const { + return 1u; + } + virtual std::size_t element_size() const { + return 1u; + } + virtual std::size_t flops() const { + return 1u; + } + virtual std::size_t frame_count() const { + return 1u; + } + virtual std::size_t transfer() const { + return 1u; + } + virtual std::string description() const { + return "no description given"; + } +}; +} } +#include +namespace ns { namespace bench { namespace units { +struct basic_unit { + virtual double eval( basic_experiment const& e + , stats::basic_stat const& s + ) const = 0; + virtual std::string unit() const = 0; +}; +} } } +#include +namespace ns { namespace bench { +struct metric { + metric(stats::basic_stat const& s, units::basic_unit const& u) + : stat_(&s), unit_(&u) { + } + metric(metric&&) = default; + metric(metric const&) = default; + double eval(basic_experiment const& e) const { + return unit_->eval(e, *stat_); + } + stats::basic_stat const& stat() const { + return *stat_; + } + units::basic_unit const& unit() const { + return *unit_; + } + private: + friend std::less; + bool operator<(metric const& other) const { + return (stat().unit() + unit().unit()) < (other.stat().unit() + other.unit().unit()); + } + private: + stats::basic_stat const* stat_; + units::basic_unit const* unit_; +}; +} } +#include +#include +namespace ns { namespace bench { namespace stats { +namespace detail { + struct min : basic_stat { + virtual double eval(stats_set const& s) const { + return boost::accumulators::min(s); + } + virtual std::string unit() const { return "(min)"; } + }; +} +const detail::min min_{}; +metric min(units::basic_unit const& u) { + return metric(min_, u); +} +} } } +#include +#include +namespace ns { namespace bench { namespace stats { +namespace detail { + struct max : basic_stat { + virtual double eval(stats_set const& s) const { + return boost::accumulators::max(s); + } + virtual std::string unit() const { return "(max)"; } + }; +} +const detail::max max_{}; +metric max(units::basic_unit const& u) { + return metric(max_, u); +} +} } } +#include +#include +namespace ns { namespace bench { namespace stats { +namespace detail { + struct mean : basic_stat { + virtual double eval(stats_set const& s) const { + return boost::accumulators::mean(s); + } + virtual std::string unit() const { return "(mean)"; } + }; +} +const detail::mean mean_{}; +metric mean(units::basic_unit const& u) { + return metric(mean_, u); +} +} } } +#include +#include +namespace ns { namespace bench { namespace stats { +namespace detail { + struct median : basic_stat { + virtual double eval(stats_set const& s) const { + if (boost::accumulators::count(times_set) < 3) { + return boost::accumulators::mean(s); + } else { + return boost::accumulators::median(s); + } + } + virtual std::string unit() const { return "(median)"; } + }; +} +const detail::median median_{}; +metric median(units::basic_unit const& u) { + return metric(median_, u); +} +} } } +namespace ns { namespace bench { namespace stats { +} } } +#include +namespace ns { namespace bench { namespace units { +namespace detail { + struct cpe : basic_unit { + virtual double eval( basic_experiment const& e + , stats::basic_stat const& s + ) const + { + double freq = ns::bench::args_map.get_or("frequency", -1); + return (freq == -1) + ? (s.eval(cycles_set) / e.size()) + : (s.eval(times_set) * freq) / (e.size() * 1e6); + } + virtual std::string unit() const { return "(cpe)"; } + }; +} +const detail::cpe cpe_{}; +} } } +#include +#include +namespace ns { namespace bench { namespace units { +namespace detail { + struct seconds { + static double value() { return 1.; } + static std::string unit() { return "s"; } + }; + struct milliseconds { + static double value() { return 1e-3; } + static std::string unit() { return "ms"; } + }; + struct nanoseconds { + static double value() { return 1e-6; } + static std::string unit() { return "ns"; } + }; +} +const detail::seconds s_{}; +const detail::milliseconds ms_{}; +const detail::nanoseconds ns_{}; +} } } +namespace ns { namespace bench { namespace units { +namespace detail { + template + struct time : basic_unit { + virtual double eval( basic_experiment const& e + , stats::basic_stat const& s + ) const + { + return s.eval(times_set) / e.size() / Seconds::value(); + } + virtual std::string unit() const { return std::string(Seconds::unit()) + "."; } + }; +} +template +const detail::time time_(Seconds) { + return {}; +} +const detail::time time_s_ = {}; +const detail::time time_ms_ = {}; +const detail::time time_ns_ = {}; +} } } +#include +namespace ns { namespace bench { namespace units { +namespace detail { + struct fps : basic_unit { + virtual double eval( basic_experiment const& e + , stats::basic_stat const& s + ) const + { + return e.frame_count() / s.eval(times_set) * 1e-6; + } + virtual std::string unit() const { return "fps"; } + }; +} +const detail::fps fps_{}; +} } } +#include +namespace ns { namespace bench { namespace units { +namespace detail { + struct gflops : basic_unit { + virtual double eval( basic_experiment const& e + , stats::basic_stat const& s + ) const + { + double freq = args_map.get("frequency"); + return e.flops() * freq / s.eval(cycles_set) * 1000000000.; + } + virtual std::string unit() const { return "GFLOPS"; } + }; +} +const detail::gflops gflops_{}; +} } } +#include +#include +namespace ns { namespace bench { namespace units { +namespace detail { + struct bytes { + static double value() { return 1.; } + static std::string unit() { return "b"; } + }; + struct kilobytes { + static double value() { return 1024. * bytes::value(); } + static std::string unit() { return "kb"; } + }; + struct megabytes { + static double value() { return 1024. * kilobytes::value(); } + static std::string unit() { return "mb"; } + }; + struct gigabytes { + static double value() { return 1024. * megabytes::value(); } + static std::string unit() { return "gb"; } + }; +} +const detail::bytes b_{}; +const detail::kilobytes kb_{}; +const detail::megabytes mb_{}; +const detail::gigabytes gb_{}; +} } } +namespace ns { namespace bench { namespace units { +namespace detail { + template < typename Bytes + , typename Seconds + > + struct transfer : units::basic_unit { + virtual double eval( basic_experiment const& e + , stats::basic_stat const& s + ) const + { + double res = 0.; + if (dynamic_cast(&s)) { + res = stats::max_.eval(times_set); + } else if (dynamic_cast(&s)) { + res = stats::min_.eval(times_set); + } else { + res = s.eval(times_set); + } + return (e.size() * e.transfer() / Bytes::value()) / (res / Seconds::value()); + } + virtual std::string unit() const { return Bytes::unit() + "/" + Seconds::unit(); } + }; +} +template +const detail::transfer transfer_(Bytes, Seconds) { + return {}; +} +const detail::transfer gb_s_ = {}; +const detail::transfer gb_ms_ = {}; +const detail::transfer gb_ns_ = {}; +const detail::transfer mb_s_ = {}; +const detail::transfer mb_ms_ = {}; +const detail::transfer mb_ns_ = {}; +const detail::transfer kb_s_ = {}; +const detail::transfer kb_ms_ = {}; +const detail::transfer kb_ns_ = {}; +const detail::transfer b_s_ = {}; +const detail::transfer b_ms_ = {}; +const detail::transfer b_ns_ = {}; +} } } +namespace ns { namespace bench { namespace units { +} } } +#include +#include +#include +#include +#include +namespace ns { namespace bench { +namespace detail { + template + struct experiment_copy { + using type = Experiment; + }; + template + struct experiment_copy::type> { + using type = Experiment&; + }; + template + struct run_experiments { + template + run_experiments(Benchmark&, Args&&...) { + } + }; + template