diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cc3882a4..898d80115 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,7 +50,6 @@ NS_prevent_in_source_build() ## ------------------------------------------------------------------------------------------------- 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" @@ -67,9 +66,7 @@ set(NS_CMAKE_PROJECT_OPTIONS ) if (DEFINED DEV) - set(NS_BENCH_GIT_TAG develop) set(BRIGAND_GIT_TAG 1.2.0) - NS_project_include(ns.bench.standalone) NS_project_include(brigand.standalone) NS_project_include(stf.standalone) endif() diff --git a/bench/function/simd/shift_left.cpp b/bench/function/simd/shift_left.cpp index ba9263320..b08ecd727 100644 --- a/bench/function/simd/shift_left.cpp +++ b/bench/function/simd/shift_left.cpp @@ -23,11 +23,11 @@ struct shlN } }; -DEFINE_SIMD_BENCH(scalar_shift_left1, shlN<1>()); -DEFINE_SIMD_BENCH(scalar_shift_left2, shlN<2>()); +DEFINE_SIMD_BENCH(simd_shift_left1, shlN<1>()); +DEFINE_SIMD_BENCH(simd_shift_left2, shlN<2>()); DEFINE_BENCH_MAIN() { - nsb::for_each(-10, 10); - nsb::for_each(-10, 10); + nsb::for_each(-10, 10); + nsb::for_each(-10, 10); } diff --git a/bench/function/simd/shift_right.cpp b/bench/function/simd/shift_right.cpp index 5a13dbaee..4f82d75ec 100644 --- a/bench/function/simd/shift_right.cpp +++ b/bench/function/simd/shift_right.cpp @@ -23,11 +23,11 @@ struct shlN } }; -DEFINE_SIMD_BENCH(scalar_shift_right1, shlN<1>()); -DEFINE_SIMD_BENCH(scalar_shift_right2, shlN<2>()); +DEFINE_SIMD_BENCH(simd_shift_right1, shlN<1>()); +DEFINE_SIMD_BENCH(simd_shift_right2, shlN<2>()); DEFINE_BENCH_MAIN() { - nsb::for_each(-10, 10); - nsb::for_each(-10, 10); + nsb::for_each(-10, 10); + nsb::for_each(-10, 10); } diff --git a/bench/function/simd/shr.cpp b/bench/function/simd/shr.cpp index 2084a7cf7..818a2a2d8 100644 --- a/bench/function/simd/shr.cpp +++ b/bench/function/simd/shr.cpp @@ -23,11 +23,11 @@ struct shlN } }; -DEFINE_SIMD_BENCH(scalar_shr1, shlN<1>()); -DEFINE_SIMD_BENCH(scalar_shr2, shlN<2>()); +DEFINE_SIMD_BENCH(simd_shr1, shlN<1>()); +DEFINE_SIMD_BENCH(simd_shr2, shlN<2>()); DEFINE_BENCH_MAIN() { - nsb::for_each(-10, 10); - nsb::for_each(-10, 10); + nsb::for_each(-10, 10); + nsb::for_each(-10, 10); } diff --git a/bench/ns.bench.hpp b/bench/ns.bench.hpp index b8d5fbe61..a1d0bcfca 100644 --- a/bench/ns.bench.hpp +++ b/bench/ns.bench.hpp @@ -96,7 +96,7 @@ struct option { } 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(), ""); + m.add_option(name(), "1"); return true; } return false; @@ -117,9 +117,10 @@ struct option { std::string usage() const { std::string u; std::string s; + std::string o = opts(); std::size_t how_many = 0; boost::char_separator sep(","); - for (auto opt : boost::tokenizer(std::string(opts()), sep)) { + for (auto opt : boost::tokenizer(o, sep)) { if (how_many) { s += " | " + opt; } else { @@ -181,7 +182,7 @@ struct auto_option { using type = typed_option; }; template -struct auto_option::value>::type> { +struct auto_option::value>::type> { using type = option; }; } } } } @@ -298,12 +299,32 @@ inline ns::bench::args::options_map& args_map() { static ns::bench::args::options_map om; return om; } +inline ns::bench::args::parser& parser() { + static ns::bench::args::parser p(""); + return p; +} inline void parse_args(int argc, char **argv) { - ns::bench::args::parser p(argv[0]); + auto& p = parser(); p.add_option("frequency", "-f,--frequency"); - p.add_option("duration" , "-d,--duration" ); - p.add_option("iteration", "-i,--iteration"); - p.add_option("loop", "-l,--loop"); + p.add_option("during" , "-d,--during" ); + p.add_option("really-during", "--really-during"); + p.add_option("iteration", "--iteration"); + p.add_option("quiet", "-q,--quiet"); + p.add_option("time-s", "--time-s"); + p.add_option("time-ms", "--time-ms"); + p.add_option("time-ns", "--time-ns"); + p.add_option("time-us", "--time-us"); + p.add_option("cpe-s", "--cpe"); + p.add_option("cpe-s", "--cpe-s"); + p.add_option("cpe-ms", "--cpe-ms"); + p.add_option("cpe-ns", "--cpe-ns"); + p.add_option("cpe-us", "--cpe-us"); + p.add_option("stddev", "--stddev"); + p.add_option("median", "--median"); + p.add_option("mean", "--mean"); + p.add_option("min", "--min"); + p.add_option("max", "--max"); + p.add_option("json", "--json"); p.parse(args_map(), argc, argv, std::nothrow); } } } @@ -320,7 +341,9 @@ double to_seconds(Duration const& d) { #include #include #include -#include +#if !defined(BOOST_COMP_MSVC_AVAILABLE) + #include +#endif #include #include #include @@ -391,6 +414,7 @@ BOOST_FORCEINLINE cycle_t read_cycles() { #include #include #include +#include #include namespace ns { namespace bench { using accumulators = boost::accumulators::stats< boost::accumulators::tag::mean @@ -398,6 +422,7 @@ namespace ns { namespace bench { , boost::accumulators::tag::min , boost::accumulators::tag::max , boost::accumulators::tag::count + , boost::accumulators::tag::variance >; using stats_set = boost::accumulators::accumulator_set; thread_local stats_set times_set, cycles_set; @@ -405,7 +430,7 @@ namespace ns { namespace bench { namespace ns { namespace bench { namespace stats { struct basic_stat { virtual double eval(stats_set const&) const = 0; - virtual std::string unit() const = 0; + virtual std::string name() const = 0; }; } } } namespace ns { namespace bench { @@ -443,7 +468,7 @@ struct basic_unit { virtual double eval( basic_experiment const& e , stats::basic_stat const& s ) const = 0; - virtual std::string unit() const = 0; + virtual std::string name() const = 0; }; } } } #include @@ -457,6 +482,9 @@ struct metric { double eval(basic_experiment const& e) const { return unit_->eval(e, *stat_); } + double result(basic_experiment const& e) const { + return eval(e) / e.element_size(); + } stats::basic_stat const& stat() const { return *stat_; } @@ -466,7 +494,7 @@ struct metric { private: friend std::less; bool operator<(metric const& other) const { - return (stat().unit() + unit().unit()) < (other.stat().unit() + other.unit().unit()); + return (stat().name() + unit().name()) < (other.stat().name() + other.unit().name()); } private: stats::basic_stat const* stat_; @@ -481,7 +509,7 @@ namespace detail { virtual double eval(stats_set const& s) const { return boost::accumulators::min(s); } - virtual std::string unit() const { return "(min)"; } + virtual std::string name() const { return "(min)"; } }; } const detail::min min_{}; @@ -497,7 +525,7 @@ namespace detail { virtual double eval(stats_set const& s) const { return boost::accumulators::max(s); } - virtual std::string unit() const { return "(max)"; } + virtual std::string name() const { return "(max)"; } }; } const detail::max max_{}; @@ -513,7 +541,7 @@ namespace detail { virtual double eval(stats_set const& s) const { return boost::accumulators::mean(s); } - virtual std::string unit() const { return "(mean)"; } + virtual std::string name() const { return "(mean)"; } }; } const detail::mean mean_{}; @@ -533,7 +561,7 @@ namespace detail { return boost::accumulators::median(s); } } - virtual std::string unit() const { return "(median)"; } + virtual std::string name() const { return "(median)"; } }; } const detail::median median_{}; @@ -541,25 +569,24 @@ metric median(units::basic_unit const& u) { return metric(median_, u); } } } } -namespace ns { namespace bench { namespace stats { -} } } +#include #include -namespace ns { namespace bench { namespace units { +#include +namespace ns { namespace bench { namespace stats { 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("frequency", -1); - return (freq == -1) - ? (s.eval(cycles_set) / e.size()) - : (s.eval(times_set) * freq) / (e.size() * 1e6); + struct stddev : basic_stat { + virtual double eval(stats_set const& s) const { + return std::sqrt(boost::accumulators::variance(s)); } - virtual std::string unit() const { return "(cpe)"; } + virtual std::string name() const { return "(stddev)"; } }; } -const detail::cpe cpe_{}; +const detail::stddev stddev_{}; +metric stddev(units::basic_unit const& u) { + return metric(stddev_, u); +} +} } } +namespace ns { namespace bench { namespace stats { } } } #include #include @@ -567,22 +594,53 @@ namespace ns { namespace bench { namespace units { namespace detail { struct seconds { static double value() { return 1.; } - static std::string unit() { return "s"; } + static std::string name() { return "s"; } }; struct milliseconds { static double value() { return 1e-3; } - static std::string unit() { return "ms"; } + static std::string name() { return "ms"; } }; - struct nanoseconds { + struct microseconds { static double value() { return 1e-6; } - static std::string unit() { return "ns"; } + static std::string name() { return "us"; } + }; + struct nanoseconds { + static double value() { return 1e-9; } + static std::string name() { return "ns"; } }; } const detail::seconds s_{}; const detail::milliseconds ms_{}; +const detail::microseconds us_{}; const detail::nanoseconds ns_{}; } } } namespace ns { namespace bench { namespace units { +namespace detail { + template + struct cpe : basic_unit { + virtual double eval( basic_experiment const& e + , stats::basic_stat const& s + ) const + { + double freq = ns::bench::args_map().get("frequency", -1); + return (freq == -1) + ? (s.eval(cycles_set) / e.size() / Seconds::value()) + : (s.eval(times_set) / e.size() * freq / Seconds::value()); + } + virtual std::string name() const { return "(cpe)"; } + }; +} +template +const detail::cpe cpe_(Seconds) { + return {}; +} +const detail::cpe cpe_s_ = {}; +const detail::cpe cpe_ms_ = {}; +const detail::cpe cpe_us_ = {}; +const detail::cpe cpe_ns_ = {}; +} } } +#include +namespace ns { namespace bench { namespace units { namespace detail { template struct time : basic_unit { @@ -592,7 +650,7 @@ namespace detail { { return s.eval(times_set) / e.size() / Seconds::value(); } - virtual std::string unit() const { return std::string(Seconds::unit()) + "."; } + virtual std::string name() const { return std::string(Seconds::name()) + "."; } }; } template @@ -601,6 +659,7 @@ const detail::time time_(Seconds) { } const detail::time time_s_ = {}; const detail::time time_ms_ = {}; +const detail::time time_us_ = {}; const detail::time time_ns_ = {}; } } } #include @@ -613,7 +672,7 @@ namespace detail { { return e.frame_count() / s.eval(times_set) * 1e-6; } - virtual std::string unit() const { return "fps"; } + virtual std::string name() const { return "fps"; } }; } const detail::fps fps_{}; @@ -629,7 +688,7 @@ namespace detail { double freq = args_map().get("frequency"); return e.flops() * freq / s.eval(cycles_set) * 1000000000.; } - virtual std::string unit() const { return "GFLOPS"; } + virtual std::string name() const { return "GFLOPS"; } }; } const detail::gflops gflops_{}; @@ -640,19 +699,19 @@ namespace ns { namespace bench { namespace units { namespace detail { struct bytes { static double value() { return 1.; } - static std::string unit() { return "b"; } + static std::string name() { return "b"; } }; struct kilobytes { static double value() { return 1024. * bytes::value(); } - static std::string unit() { return "kb"; } + static std::string name() { return "kb"; } }; struct megabytes { static double value() { return 1024. * kilobytes::value(); } - static std::string unit() { return "mb"; } + static std::string name() { return "mb"; } }; struct gigabytes { static double value() { return 1024. * megabytes::value(); } - static std::string unit() { return "gb"; } + static std::string name() { return "gb"; } }; } const detail::bytes b_{}; @@ -680,7 +739,7 @@ namespace detail { } return (e.size() * e.transfer() / Bytes::value()) / (res / Seconds::value()); } - virtual std::string unit() const { return Bytes::unit() + "/" + Seconds::unit(); } + virtual std::string name() const { return Bytes::name() + "/" + Seconds::name(); } }; } template @@ -691,32 +750,37 @@ 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 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 kb_ms_ = {}; +const detail::transfer kb_ns_ = {}; const detail::transfer b_s_ = {}; -const detail::transfer b_ms_ = {}; -const detail::transfer b_ns_ = {}; +const detail::transfer b_ms_ = {}; +const detail::transfer b_ns_ = {}; } } } namespace ns { namespace bench { namespace units { } } } namespace ns { namespace bench { namespace detail { template struct range {}; template struct make_range {}; +template <> struct make_range<0> { using type = range<>; }; template <> struct make_range<1> { using type = range<0>; }; template <> struct make_range<2> { using type = range<0, 1>; }; template <> struct make_range<3> { using type = range<0, 1, 2>; }; template <> struct make_range<4> { using type = range<0, 1, 2, 3>; }; template <> struct make_range<5> { using type = range<0, 1, 2, 3, 4>; }; template <> struct make_range<6> { using type = range<0, 1, 2, 3, 4, 5>; }; +template <> struct make_range<7> { using type = range<0, 1, 2, 3, 4, 5, 6>; }; +template <> struct make_range<8> { using type = range<0, 1, 2, 3, 4, 5, 6, 7>; }; +template <> struct make_range<9> { using type = range<0, 1, 2, 3, 4, 5, 6, 7, 8>; }; template using make_range_t = typename make_range::type; #define NS_BENCH_STATIC_UNROLL(f, ...) \ (void)std::initializer_list{((void)f(__VA_ARGS__), 0)...} \ } } } +#include #include #include #include @@ -733,11 +797,45 @@ struct result_info { std::string stat; std::string desc; std::size_t size; + std::size_t element_size; + std::size_t flops; + std::size_t frame_count; + std::size_t transfer; std::size_t samples; }; struct results { using container_type = std::vector>>; using contained_type = typename container_type::value_type; + using optional_infos_container_type = std::unordered_map; + results() { + comp_ = BOOST_COMPILER; + plat_ = BOOST_PLATFORM; + std::string arch = "(unknown)"; + #if BOOST_ARCH_X86_64 + arch = "x86_64"; + #endif + #if BOOST_ARCH_X86_32 + arch = "x86"; + #endif + #if BOOST_ARCH_PPC + arch = "ppc"; + if (sizeof(void*) == 8) { + arch += "64"; + } + #if BOOST_ENDIAN_BIG_BYTE + arch += "be"; + #else + arch += "le"; + #endif + #endif + #if BOOST_ARCH_ARM + arch = "arm"; + #endif + #if defined(__aarch64__) + arch = "aarch64"; + #endif + arch_ = arch; + } void update(std::string const& name, result_info const& r) { auto found = std::find_if(results_.begin(), results_.end(), [&](contained_type const& v) { @@ -754,8 +852,24 @@ struct results { container_type::const_iterator cbegin() const { return results_.cbegin(); } container_type::const_iterator end() const { return results_.end(); } container_type::const_iterator cend() const { return results_.cend(); } + std::string const& compiler() const { return comp_; } + std::string const& platform() const { return plat_; } + std::string const& architecture() const { return arch_; } + results& add_optional_info(std::string const& key, std::string const& value) + { + opts_.emplace(key, value); + return *this; + } + optional_infos_container_type const& optional_infos() const + { + return opts_; + } private: + std::string comp_; + std::string plat_; + std::string arch_; container_type results_; + std::unordered_map opts_; }; template std::string make_generators_desc(detail::range const&, std::tuple const& gs) { @@ -766,23 +880,91 @@ std::string make_generators_desc(detail::range const&, std::tuple c desc += d; first = false; }; + (void)append; NS_BENCH_STATIC_UNROLL(append, std::get(gs).description()); return desc; } template result_info make_result_info(Experiment const& e, Metric const& m, std::tuple const& gs) { result_info r; - r.size = e.size(); - r.unit = m.unit().unit(); - r.stat = m.stat().unit(); - r.desc = make_generators_desc(detail::make_range_t(), gs); - r.result = m.eval(e) / e.element_size(); - r.samples = boost::accumulators::count(times_set); + r.size = e.size(); + r.element_size = e.element_size(); + r.flops = e.flops(); + r.frame_count = e.frame_count(); + r.transfer = e.transfer(); + r.unit = m.unit().name(); + r.stat = m.stat().name(); + r.desc = make_generators_desc(detail::make_range_t(), gs); + r.result = m.result(e); + r.samples = boost::accumulators::count(times_set); return r; } -std::ostream& operator<<(std::ostream& os, result_info const& r) +std::ostream& operator<<(std::ostream& os, result_info const& r); +std::ostream& operator<<(std::ostream& os, results const& r); +std::string as_str(std::string const& what) +{ + return "\"" + what + "\""; +} +template +std::string as_json(std::string const& key, Val const& val) +{ + std::stringstream ss; + ss << key << ": " << val; + return ss.str(); +} +std::string as_json(result_info const& r) +{ + std::stringstream ss; + ss << "{"; + ss << " " << as_json(as_str("size"), r.size); + ss << ", " << as_json(as_str("element_size"), r.element_size); + ss << ", " << as_json(as_str("flops"), r.flops); + ss << ", " << as_json(as_str("frame_count"), r.frame_count); + ss << ", " << as_json(as_str("transfer"), r.transfer); + ss << ", " << as_json(as_str("stat"), as_str(r.stat)); + ss << ", " << as_json(as_str("unit"), as_str(r.unit)); + ss << ", " << as_json(as_str("result"), r.result); + ss << ", " << as_json(as_str("samples"), r.samples); + ss << ", " << as_json(as_str("description"), as_str(r.desc)); + ss << "}"; + return ss.str(); +} +std::string as_json(results const& r) +{ + std::stringstream ss; + ss << "{ "; + bool first = true; + for (auto const& b : r) { + if (!first) ss << ", "; + auto const& name = b.first; + auto const& infos = b.second; + ss << as_str(name) << ": {"; + ss << " " << as_json(as_str("compiler"), as_str(r.compiler())); + ss << ", " << as_json(as_str("platform"), as_str(r.platform())); + ss << ", " << as_json(as_str("architecture"), as_str(r.architecture())); + for (auto const& o : r.optional_infos()) { + ss << ", " << as_json(as_str(o.first), as_str(o.second)); + } + { + ss << ", " << as_str("benchmarks") << ": [ "; + bool first2 = true; + for (auto const& info : infos) { + if (!first2) ss << ", "; + ss << as_json(info); + first2 = false; + } + ss << " ]"; + } + ss << " }"; + first = false; + } + ss << " }"; + return ss.str(); +} +std::string as_table(result_info const& r) { - os + std::stringstream ss; + ss << std::setw(size_maxw) << r.size << ' ' << std::setw(result_maxw) @@ -796,10 +978,11 @@ std::ostream& operator<<(std::ostream& os, result_info const& r) << std::setw(description_maxw) << r.desc ; - return os; + return ss.str(); } -std::ostream& operator<<(std::ostream& os, results const& r) +std::string as_table(results const& r) { + std::stringstream ss; std::size_t bar_size = 0; bar_size += unit_maxw + 1; bar_size += stat_maxw + 1; @@ -808,15 +991,15 @@ std::ostream& operator<<(std::ostream& os, results const& r) bar_size += samples_maxw + 1; bar_size += description_maxw + 1; auto print_bar = [&](std::size_t size, char what) { - os << ":: "; - for (std::size_t i = 0; i < size; ++i) os << what; - os << std::endl; + ss << ":: "; + for (std::size_t i = 0; i < size; ++i) ss << what; + ss << std::endl; }; for (auto const& b : r) { auto const& name = b.first; auto const& infos = b.second; - os << "::: Benchmarking results for: \"" << name << "\"" << std::endl; - os + ss << ":: Benchmarking results for: \"" << name << "\"" << std::endl; + ss << std::setw(size_maxw) << "Size" << ' ' << std::setw(result_maxw) @@ -832,10 +1015,28 @@ std::ostream& operator<<(std::ostream& os, results const& r) << std::endl ; for (auto const& info : infos) { - os << info << std::endl; + ss << info << std::endl; } print_bar(bar_size, '-'); } + return ss.str(); +} +std::ostream& operator<<(std::ostream& os, result_info const& r) +{ + if (args_map().get("json", false)) { + os << as_json(r); + } else { + os << as_table(r); + } + return os; +} +std::ostream& operator<<(std::ostream& os, results const& r) +{ + if (args_map().get("json", false)) { + os << as_json(r); + } else { + os << as_table(r); + } return os; } } } @@ -935,12 +1136,15 @@ class setup { setup& median(units::basic_unit const& u) { return metric(stats::median_, u); } + setup& stddev(units::basic_unit const& u) { + return metric(stats::stddev_, u); + } std::set<::ns::bench::metric> const& metrics() const { return metrics_; } - static std::size_t internal_loop_size() { - static const std::size_t default_internal_loop_size = 1024u; - return args_map().get("loop", default_internal_loop_size); + static std::size_t& internal_loop_size() { + static std::size_t sz = 1024u; + return sz; } bool has_during() const { return static_cast(during_); } bool has_iteration() const { return static_cast(iteration_); } @@ -954,6 +1158,38 @@ class setup { boost::optional iteration_; std::set<::ns::bench::metric> metrics_; }; +setup default_setup() +{ + setup s; + auto m = args_map(); + auto const& default_unit = units::cpe_s_; + typedef setup& (ns::bench::setup::*met_t)(ns::bench::units::basic_unit const&); + auto update = [&m, &s, default_unit](met_t f) { + if (m.get("cpe", false)) s = (s.*f)(units::cpe_s_); + else if (m.get("cpe-s", false)) s = (s.*f)(units::cpe_s_); + else if (m.get("cpe-ms", false)) s = (s.*f)(units::cpe_ms_); + else if (m.get("cpe-ns", false)) s = (s.*f)(units::cpe_ns_); + else if (m.get("cpe-us", false)) s = (s.*f)(units::cpe_us_); + else if (m.get("time-ms", false)) s = (s.*f)(units::time_ms_); + else if (m.get("time-us", false)) s = (s.*f)(units::time_us_); + else if (m.get("time-ns", false)) s = (s.*f)(units::time_ns_); + else if (m.get("time-s", false)) s = (s.*f)(units::time_s_); + else s = (s.*f)(units::cpe_s_); + }; + if (m.get("stddev", false)) update(&ns::bench::setup::stddev); + if (m.get("median", false)) update(&ns::bench::setup::median); + if (m.get("mean", false)) update(&ns::bench::setup::mean); + if (m.get("min", false)) update(&ns::bench::setup::min); + if (m.get("max", false)) update(&ns::bench::setup::max); + if (m.has("during")) s = s.during(m.get("during")); + else if (m.has("really-during")) s = s.really_during(m.get("really-during")); + else if (m.has("iteration")) s = s.iteration(m.get("iteration")); + else s = s.really_during(1.); + if (s.metrics().empty()) { + s = s.median(default_unit); + } + return s; +} } } #define NS_BENCH_AUTO_DECLTYPE(...) \ -> decltype(__VA_ARGS__) { return (__VA_ARGS__); } \ @@ -976,13 +1212,38 @@ BOOST_FORCEINLINE void dnopt(T const& v) { } #endif } } +#include +#include +#include +#include +#include +#include +#include +#include +namespace ns { namespace bench { namespace generators { +template +struct fixed +{ + fixed(T const& val) : val_(val) + { + } + inline T const& operator()() { + return val_; + } + std::string description() const { + return ""; + } + private: + T val_; +}; +} } } #include #include #include namespace ns { namespace bench { namespace detail { template -void prepare_parameter(std::vector& val, G& g, std::size_t sz) +void prepare_parameter(std::vector& val, G& g, std::size_t sz) { val.resize(sz); val.clear(); @@ -990,6 +1251,11 @@ void prepare_parameter(std::vector& val, G& g, std::size_t sz) val[i] = g(); } } +template +void prepare_parameter(generators::fixed*& val, generators::fixed& g, std::size_t) +{ + val = &g; +} template void prepare_parameters( range const& , std::tuple& ps @@ -997,6 +1263,7 @@ void prepare_parameters( range const& , std::size_t sz ) { + (void)sz; NS_BENCH_STATIC_UNROLL(prepare_parameter, std::get(ps), std::get(gs), sz); } template @@ -1007,12 +1274,25 @@ void prepare_parameters( std::tuple& ps { prepare_parameters(make_range_t(), ps, gs, sz); } +template +T container_at(std::vector const& v, std::size_t i) +{ + return v[i]; +} +template +T const& container_at(generators::fixed* const& g, std::size_t) +{ + return (*g)(); +} template BOOST_FORCEINLINE auto call(range const&, F f, std::tuple const& args, std::size_t i) -NS_BENCH_AUTO_DECLTYPE(f(std::get(args)[i]...)) +NS_BENCH_AUTO_DECLTYPE(f(container_at(std::get(args), i)...)) template BOOST_FORCEINLINE auto call(F f, std::tuple const& args, std::size_t i) NS_BENCH_AUTO_DECLTYPE(call(make_range_t(), f, args, i)) +template +BOOST_FORCEINLINE auto call(F f, std::tuple<> const&, std::size_t) +NS_BENCH_AUTO_DECLTYPE(f()) template struct experiment_maybe_copy { using type = Experiment; @@ -1023,19 +1303,36 @@ struct experiment_maybe_copy using experiment_maybe_copy_t = typename experiment_maybe_copy::type; -template -struct real_align : boost::alignment::alignment_of +template +struct parameter_alignment +: boost::alignment::alignment_of {}; -template -struct real_align::type> - : std::integral_constant +template +struct parameter_alignment::type> +: std::integral_constant {}; -template -using make_vec_t = std::vector::value>>; +template +struct make_parameters_container { + using type = + std::vector::value>>; +}; +template +struct make_parameters_container, T const&> { + using type = generators::fixed*; +}; +template +using make_parameters_container_t = + typename make_parameters_container::type, T>::type; +template +struct generator_value { + using type = decltype(std::declval()()); +}; +template +using generator_value_t = typename generator_value::type; } template void run(results& r, setup const& s, std::string const& name, Experiment&& e, Gs&&... g) { - static_assert( std::is_base_of::value + static_assert( std::is_base_of::type>::value , "Experiment must inherits from experiment base class!" ); double time = 0.; @@ -1048,20 +1345,21 @@ void run(results& r, setup const& s, std::string const& name, Experiment&& e, Gs if (s.has_during()) max_time = s.during(); if (s.has_iteration()) max_iter = s.iteration(); if (s.has_really_during()) max_real_time = s.really_during(); + bool is_quiet = args_map().get("quiet", false); auto do_continue = [&]() { auto elapsed_real_time = to_seconds(time_clock::now() - real_starting_time); auto elapsed_dot_time = to_seconds(time_clock::now() - dot_time); - if (elapsed_dot_time >= 1.) { + if (!is_quiet && elapsed_dot_time >= 1.) { dot_time = time_clock::now(); std::cerr << "."; } if (s.has_really_during() && elapsed_real_time >= max_real_time) { return false; } - if (s.has_iteration() && iter > max_iter) { + if (s.has_iteration() && iter >= max_iter) { return false; } - if (s.has_during() && time > max_time) { + if (s.has_during() && time >= max_time) { return false; } if (!s.has_during() && !s.has_iteration() && !s.has_really_during()) { @@ -1075,10 +1373,10 @@ void run(results& r, setup const& s, std::string const& name, Experiment&& e, Gs }; times_set = stats_set(); cycles_set = stats_set(); - auto ps = std::tuple...>(); + std::tuple>...> ps; auto gs = std::make_tuple(std::forward(g)...); - auto sz = ::ns::bench::setup::internal_loop_size(); - std::cerr << ":: Benchmarking: \"" << name << "\" "; + auto sz = args_map().get("loop", ::ns::bench::setup::internal_loop_size()); + if (!is_quiet) std::cerr << ":: Benchmarking: \"" << name << "\" "; while (do_continue()) { detail::experiment_maybe_copy_t local(e); detail::prepare_parameters(ps, gs, sz); @@ -1095,7 +1393,7 @@ void run(results& r, setup const& s, std::string const& name, Experiment&& e, Gs cycles_set(elapsed_cycles); do_step(elapsed_time); } - std::cerr << std::endl; + if (!is_quiet) std::cerr << std::endl; for (auto const& m : s.metrics()) { r.update(name, make_result_info(e, m, gs)); } @@ -1129,16 +1427,11 @@ struct rand ) { if (std::is_unsigned::value) { - if (pmin < 0) pmin = 0; pmin = std::abs(pmin); pmax = std::abs(pmax); } - if (pmin > pmax) std::swap(pmin, pmax); - if (pmin == pmax) { - if (pmax == 0) pmax = 10; - pmin = T(0); - } + if (pmin == pmax) pmin = T(0); min_ = pmin; max_ = pmax; } diff --git a/bench/simd_bench.hpp b/bench/simd_bench.hpp index f46fc7b9e..a1250081a 100644 --- a/bench/simd_bench.hpp +++ b/bench/simd_bench.hpp @@ -35,10 +35,7 @@ struct format_type> ns::bench::setup setup() { namespace nsb = ns::bench; - return nsb::setup() - .median(nsb::units::cpe_) - .really_during(2.) - ; + return nsb::default_setup(); } ns::bench::results& results() @@ -47,6 +44,11 @@ ns::bench::results& results() return r; } +inline bool is_quiet() +{ + return ns::bench::args_map().get("quiet", false); +} + void print_results() { std::cout << results() << std::endl; @@ -82,7 +84,9 @@ struct bench_experiment : ns::bench::experiment void which_type() { - std::cout << ":: [T = " << nsb::type_id() << "]" << std::endl; + if (!is_quiet()) { + std::cout << ":: [T = " << nsb::type_id() << "]" << std::endl; + } } template @@ -124,13 +128,15 @@ struct bench_experiment : ns::bench::experiment void describe() { - std::cout << "::- --------------------------------------------------------------------------------------------------------------------------------------------------"; - std::cout << std::endl; - std::cout << "::- Compiler: " << BOOST_COMPILER << std::endl; - std::cout << "::- Platform: " << BOOST_PLATFORM << std::endl; - std::cout << "::- SIMD: " << nsb::type_id() << std::endl; - std::cout << "::- --------------------------------------------------------------------------------------------------------------------------------------------------"; - std::cout << std::endl; + if (!is_quiet()) { + std::cout << ":: --------------------------------------------------------------------------------------------------------------------------------------------------"; + std::cout << std::endl; + std::cout << ":: Compiler: " << BOOST_COMPILER << std::endl; + std::cout << ":: Platform: " << BOOST_PLATFORM << std::endl; + std::cout << ":: SIMD: " << nsb::type_id() << std::endl; + std::cout << ":: --------------------------------------------------------------------------------------------------------------------------------------------------"; + std::cout << std::endl; + } } template struct template_of; @@ -152,6 +158,29 @@ using scalar_experiment = , 1 >; +template +using unrolled_pack = boost::simd::pack::static_size * N>; + +template +using unrolled_simd_experiment = + bench_experiment< Experiment + , unrolled_pack, N> + , unrolled_pack, N>::static_size + >; + +std::string sanitized_simd() +{ + auto s = nsb::type_id(); + auto bsns = std::string("boost::simd::"); + if (s.find(bsns) != std::string::npos) { + s = s.substr(bsns.size(), s.size()); + } + if (s[s.size() - 1] == '_') { + s.resize(s.size() - 1); + } + return s; +} + #define DEFINE_BENCH(name_, f, experiment) \ template \ struct name_ : experiment> \ @@ -161,8 +190,16 @@ using scalar_experiment = } \ /**/ -#define DEFINE_SIMD_BENCH(name, f) DEFINE_BENCH(name, f, simd_experiment) -#define DEFINE_SCALAR_BENCH(name, f) DEFINE_BENCH(name, f, scalar_experiment) +#define DEFINE_SIMD_BENCH(name, f) DEFINE_BENCH(name, f, simd_experiment) +#define DEFINE_SCALAR_BENCH(name, f) DEFINE_BENCH(name, f, scalar_experiment) +#define DEFINE_UNROLLED_SIMD_BENCH(name_, unroll, f)\ + template \ + struct name_ : unrolled_simd_experiment> \ + { \ + static const char* name() { return BOOST_PP_STRINGIZE(name_); } \ + static decltype(f) functor() { return f; } \ + } \ +/**/ #define DEFINE_BENCH_MAIN() \ void main2(); \ @@ -171,6 +208,7 @@ using scalar_experiment = nsb::parse_args(argc, argv); \ main2(); \ describe(); \ + results().add_optional_info("simd", sanitized_simd()); \ print_results(); \ return 0; \ } \