From 016532f011605dec459d65c4fe309e31606dfc1c Mon Sep 17 00:00:00 2001 From: Hans Dembinski Date: Mon, 19 Aug 2019 23:55:43 +0200 Subject: [PATCH] Histogram fill method (#54) huge refactor, various speed improvements, potential for further improvements and parallelization --- benchmark/CMakeLists.txt | 4 +- benchmark/generator.hpp | 11 +- benchmark/histogram_filling.cpp | 86 ++++ benchmark/histogram_filling_experiments.cpp | 112 +---- benchmark/histogram_filling_gsl.cpp | 2 + benchmark/histogram_filling_root.cpp | 4 + doc/Jamfile | 2 +- examples/guide_histogram_streaming.cpp | 37 +- include/boost/histogram/detail/args_type.hpp | 9 +- include/boost/histogram/detail/at.hpp | 65 +++ include/boost/histogram/detail/attribute.hpp | 16 - include/boost/histogram/detail/axes.hpp | 122 +++-- include/boost/histogram/detail/data.hpp | 48 ++ include/boost/histogram/detail/detect.hpp | 96 ++-- include/boost/histogram/detail/fill.hpp | 345 ++++++++++++++ include/boost/histogram/detail/fill_n.hpp | 291 ++++++++++++ include/boost/histogram/detail/linearize.hpp | 421 +++--------------- .../detail/non_member_container_access.hpp | 70 +++ .../boost/histogram/detail/optional_index.hpp | 47 ++ include/boost/histogram/detail/span.hpp | 249 +++++++++++ include/boost/histogram/fwd.hpp | 14 +- include/boost/histogram/histogram.hpp | 176 ++++++-- include/boost/histogram/indexed.hpp | 22 +- include/boost/histogram/ostream.hpp | 11 +- test/CMakeLists.txt | 19 +- test/Jamfile | 5 +- test/detail_axes_test.cpp | 25 +- test/detail_detect_test.cpp | 2 +- test/detail_linearize_test.cpp | 64 --- test/detail_meta_test.cpp | 367 --------------- test/detail_misc_test.cpp | 57 ++- test/detail_static_if_test.cpp | 33 ++ test/histogram_custom_axis_test.cpp | 132 ++++++ test/histogram_fill_test.cpp | 299 +++++++++++++ test/histogram_growing_test.cpp | 2 +- test/histogram_operators_test.cpp | 19 +- test/histogram_test.cpp | 160 ++----- test/unlimited_storage_test.cpp | 3 + tools/check_build_system.py | 1 + 39 files changed, 2216 insertions(+), 1232 deletions(-) create mode 100644 include/boost/histogram/detail/at.hpp delete mode 100644 include/boost/histogram/detail/attribute.hpp create mode 100644 include/boost/histogram/detail/data.hpp create mode 100644 include/boost/histogram/detail/fill.hpp create mode 100644 include/boost/histogram/detail/fill_n.hpp create mode 100644 include/boost/histogram/detail/non_member_container_access.hpp create mode 100644 include/boost/histogram/detail/optional_index.hpp create mode 100644 include/boost/histogram/detail/span.hpp delete mode 100644 test/detail_linearize_test.cpp delete mode 100644 test/detail_meta_test.cpp create mode 100644 test/detail_static_if_test.cpp create mode 100644 test/histogram_custom_axis_test.cpp create mode 100644 test/histogram_fill_test.cpp diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 1f227342..77d7c9ee 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -47,8 +47,8 @@ boost_fetch(hdembinski/benchmark) macro(add_benchmark name) add_executable(${name} "${name}.cpp") - target_compile_options(${name} - PRIVATE -DNDEBUG -O3 ${BENCHMARK_FLAGS} -funsafe-math-optimizations) + target_compile_options(${name} PRIVATE + -DNDEBUG -O3 -march=native ${BENCHMARK_FLAGS} -funsafe-math-optimizations) target_link_libraries(${name} PRIVATE Boost::histogram benchmark_main) endmacro() diff --git a/benchmark/generator.hpp b/benchmark/generator.hpp index e45f19d5..f613ced3 100644 --- a/benchmark/generator.hpp +++ b/benchmark/generator.hpp @@ -30,20 +30,21 @@ uniform_int init(int n) { } template -struct generator { +struct generator : std::array { + using base_t = std::array; + template generator(Ts... ts) { std::default_random_engine rng(1); auto dis = init(ts...); - std::generate(buffer_, buffer_ + N, [&] { return dis(rng); }); + std::generate(base_t::begin(), base_t::end(), [&] { return dis(rng); }); } const double& operator()() { ++ptr_; - if (ptr_ == buffer_ + N) ptr_ = buffer_; + if (ptr_ == base_t::data() + N) ptr_ = base_t::data(); return *ptr_; } - double buffer_[N]; - const double* ptr_ = buffer_ - 1; + const double* ptr_ = base_t::data() - 1; }; diff --git a/benchmark/histogram_filling.cpp b/benchmark/histogram_filling.cpp index 8259f23f..406b32ba 100644 --- a/benchmark/histogram_filling.cpp +++ b/benchmark/histogram_filling.cpp @@ -38,6 +38,15 @@ static void fill_1d(benchmark::State& state) { auto h = make_s(Tag(), Storage(), reg(100, 0, 1)); auto gen = generator(); for (auto _ : state) benchmark::DoNotOptimize(h(gen())); + state.SetItemsProcessed(state.iterations()); +} + +template +static void fill_n_1d(benchmark::State& state) { + auto h = make_s(Tag(), Storage(), reg(100, 0, 1)); + auto gen = generator(); + for (auto _ : state) h.fill(gen); + state.SetItemsProcessed(state.iterations() * gen.size()); } template @@ -45,6 +54,16 @@ static void fill_2d(benchmark::State& state) { auto h = make_s(Tag(), Storage(), reg(100, 0, 1), reg(100, 0, 1)); auto gen = generator(); for (auto _ : state) benchmark::DoNotOptimize(h(gen(), gen())); + state.SetItemsProcessed(state.iterations() * 2); +} + +template +static void fill_n_2d(benchmark::State& state) { + auto h = make_s(Tag(), Storage(), reg(100, 0, 1), reg(100, 0, 1)); + auto gen = generator(); + auto v = {gen, gen}; + for (auto _ : state) h.fill(v); + state.SetItemsProcessed(state.iterations() * 2 * gen.size()); } template @@ -52,6 +71,16 @@ static void fill_3d(benchmark::State& state) { auto h = make_s(Tag(), Storage(), reg(100, 0, 1), reg(100, 0, 1), reg(100, 0, 1)); auto gen = generator(); for (auto _ : state) benchmark::DoNotOptimize(h(gen(), gen(), gen())); + state.SetItemsProcessed(state.iterations() * 3); +} + +template +static void fill_n_3d(benchmark::State& state) { + auto h = make_s(Tag(), Storage(), reg(100, 0, 1), reg(100, 0, 1), reg(100, 0, 1)); + auto gen = generator(); + auto v = {gen, gen, gen}; + for (auto _ : state) h.fill(v); + state.SetItemsProcessed(state.iterations() * 3 * gen.size()); } template @@ -61,38 +90,95 @@ static void fill_6d(benchmark::State& state) { auto gen = generator(); for (auto _ : state) benchmark::DoNotOptimize(h(gen(), gen(), gen(), gen(), gen(), gen())); + state.SetItemsProcessed(state.iterations() * 6); +} + +template +static void fill_n_6d(benchmark::State& state) { + auto h = make_s(Tag(), Storage(), reg(10, 0, 1), reg(10, 0, 1), reg(10, 0, 1), + reg(10, 0, 1), reg(10, 0, 1), reg(10, 0, 1)); + auto gen = generator(); + auto v = {gen, gen, gen, gen, gen, gen}; + for (auto _ : state) h.fill(v); + state.SetItemsProcessed(state.iterations() * 6 * gen.size()); } BENCHMARK_TEMPLATE(fill_1d, uniform, static_tag, SStore); BENCHMARK_TEMPLATE(fill_1d, uniform, static_tag, DStore); BENCHMARK_TEMPLATE(fill_1d, uniform, dynamic_tag, SStore); BENCHMARK_TEMPLATE(fill_1d, uniform, dynamic_tag, DStore); + +BENCHMARK_TEMPLATE(fill_n_1d, uniform, static_tag, SStore); +BENCHMARK_TEMPLATE(fill_n_1d, uniform, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_1d, uniform, dynamic_tag, SStore); +BENCHMARK_TEMPLATE(fill_n_1d, uniform, dynamic_tag, DStore); + BENCHMARK_TEMPLATE(fill_2d, uniform, static_tag, SStore); BENCHMARK_TEMPLATE(fill_2d, uniform, static_tag, DStore); BENCHMARK_TEMPLATE(fill_2d, uniform, dynamic_tag, SStore); BENCHMARK_TEMPLATE(fill_2d, uniform, dynamic_tag, DStore); + +BENCHMARK_TEMPLATE(fill_n_2d, uniform, static_tag, SStore); +BENCHMARK_TEMPLATE(fill_n_2d, uniform, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_2d, uniform, dynamic_tag, SStore); +BENCHMARK_TEMPLATE(fill_n_2d, uniform, dynamic_tag, DStore); + BENCHMARK_TEMPLATE(fill_3d, uniform, static_tag, SStore); BENCHMARK_TEMPLATE(fill_3d, uniform, static_tag, DStore); BENCHMARK_TEMPLATE(fill_3d, uniform, dynamic_tag, SStore); BENCHMARK_TEMPLATE(fill_3d, uniform, dynamic_tag, DStore); + +BENCHMARK_TEMPLATE(fill_n_3d, uniform, static_tag, SStore); +BENCHMARK_TEMPLATE(fill_n_3d, uniform, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_3d, uniform, dynamic_tag, SStore); +BENCHMARK_TEMPLATE(fill_n_3d, uniform, dynamic_tag, DStore); + BENCHMARK_TEMPLATE(fill_6d, uniform, static_tag, SStore); BENCHMARK_TEMPLATE(fill_6d, uniform, static_tag, DStore); BENCHMARK_TEMPLATE(fill_6d, uniform, dynamic_tag, SStore); BENCHMARK_TEMPLATE(fill_6d, uniform, dynamic_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_6d, uniform, static_tag, SStore); +BENCHMARK_TEMPLATE(fill_n_6d, uniform, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_6d, uniform, dynamic_tag, SStore); +BENCHMARK_TEMPLATE(fill_n_6d, uniform, dynamic_tag, DStore); + BENCHMARK_TEMPLATE(fill_1d, normal, static_tag, SStore); BENCHMARK_TEMPLATE(fill_1d, normal, static_tag, DStore); BENCHMARK_TEMPLATE(fill_1d, normal, dynamic_tag, SStore); BENCHMARK_TEMPLATE(fill_1d, normal, dynamic_tag, DStore); + +BENCHMARK_TEMPLATE(fill_n_1d, normal, static_tag, SStore); +BENCHMARK_TEMPLATE(fill_n_1d, normal, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_1d, normal, dynamic_tag, SStore); +BENCHMARK_TEMPLATE(fill_n_1d, normal, dynamic_tag, DStore); + BENCHMARK_TEMPLATE(fill_2d, normal, static_tag, SStore); BENCHMARK_TEMPLATE(fill_2d, normal, static_tag, DStore); BENCHMARK_TEMPLATE(fill_2d, normal, dynamic_tag, SStore); BENCHMARK_TEMPLATE(fill_2d, normal, dynamic_tag, DStore); + +BENCHMARK_TEMPLATE(fill_n_2d, normal, static_tag, SStore); +BENCHMARK_TEMPLATE(fill_n_2d, normal, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_2d, normal, dynamic_tag, SStore); +BENCHMARK_TEMPLATE(fill_n_2d, normal, dynamic_tag, DStore); + BENCHMARK_TEMPLATE(fill_3d, normal, static_tag, SStore); BENCHMARK_TEMPLATE(fill_3d, normal, static_tag, DStore); BENCHMARK_TEMPLATE(fill_3d, normal, dynamic_tag, SStore); BENCHMARK_TEMPLATE(fill_3d, normal, dynamic_tag, DStore); + +BENCHMARK_TEMPLATE(fill_n_3d, normal, static_tag, SStore); +BENCHMARK_TEMPLATE(fill_n_3d, normal, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_3d, normal, dynamic_tag, SStore); +BENCHMARK_TEMPLATE(fill_n_3d, normal, dynamic_tag, DStore); + BENCHMARK_TEMPLATE(fill_6d, normal, static_tag, SStore); BENCHMARK_TEMPLATE(fill_6d, normal, static_tag, DStore); BENCHMARK_TEMPLATE(fill_6d, normal, dynamic_tag, SStore); BENCHMARK_TEMPLATE(fill_6d, normal, dynamic_tag, DStore); + +BENCHMARK_TEMPLATE(fill_n_6d, normal, static_tag, SStore); +BENCHMARK_TEMPLATE(fill_n_6d, normal, static_tag, DStore); +BENCHMARK_TEMPLATE(fill_n_6d, normal, dynamic_tag, SStore); +BENCHMARK_TEMPLATE(fill_n_6d, normal, dynamic_tag, DStore); diff --git a/benchmark/histogram_filling_experiments.cpp b/benchmark/histogram_filling_experiments.cpp index 8e76b1c8..6647404f 100644 --- a/benchmark/histogram_filling_experiments.cpp +++ b/benchmark/histogram_filling_experiments.cpp @@ -33,16 +33,8 @@ auto make_storage(const U& axes) { return std::vector(detail::bincount(axes), 0); } -template -auto make_strides(const T& axes) { - std::vector strides(detail::axes_rank(axes) + 1, 1); - auto sit = strides.begin(); - detail::for_each_axis(axes, [&](const auto& a) { *++sit *= axis::traits::extent(a); }); - return strides; -} - template -void fill_b(const Axes& axes, Storage& storage, const Tuple& t) { +void fill_a(const Axes& axes, Storage& storage, const Tuple& t) { using namespace boost::mp11; std::size_t stride = 1, index = 0; mp_for_each>>([&](auto i) { @@ -54,60 +46,22 @@ void fill_b(const Axes& axes, Storage& storage, const Tuple& t) { ++storage[index]; } -template -void fill_c(const Axes& axes, const std::size_t* strides, Storage& storage, - const Tuple& t) { - using namespace boost::mp11; - std::size_t index = 0; - BOOST_ASSERT(boost::histogram::detail::axes_rank(axes) == - boost::histogram::detail::axes_rank(t)); - mp_for_each>>([&](auto i) { - const auto& a = boost::histogram::detail::axis_get(axes); - const auto& v = std::get(t); - index += (a.index(v) + 1) * *strides++; - }); - ++storage[index]; -} - template static void fill_1d_a(benchmark::State& state) { auto axes = std::make_tuple(reg(100, 0, 1)); generator gen; auto storage = make_storage(axes); - for (auto _ : state) { - const auto i = std::get<0>(axes).index(gen()); - ++storage[i + 1]; - } + for (auto _ : state) fill_a(axes, storage, std::forward_as_tuple(gen())); + state.SetItemsProcessed(state.iterations()); } template -static void fill_1d_b(benchmark::State& state) { - auto axes = std::make_tuple(reg(100, 0, 1)); - generator gen; - auto storage = make_storage(axes); - for (auto _ : state) { fill_b(axes, storage, std::forward_as_tuple(gen())); } -} - -template -static void fill_1d_c(benchmark::State& state) { - auto axes = std::make_tuple(reg(100, 0, 1)); - generator gen; - auto storage = make_storage(axes); - auto strides = make_strides(axes); - for (auto _ : state) { - fill_c(axes, strides.data(), storage, std::forward_as_tuple(gen())); - } -} - -template -static void fill_1d_c_dyn(benchmark::State& state) { +static void fill_1d_a_dyn(benchmark::State& state) { auto axes = vector_of_variant({reg(100, 0, 1)}); generator gen; auto storage = make_storage(axes); - auto strides = make_strides(axes); - for (auto _ : state) { - fill_c(axes, strides.data(), storage, std::forward_as_tuple(gen())); - } + for (auto _ : state) fill_a(axes, storage, std::forward_as_tuple(gen())); + state.SetItemsProcessed(state.iterations()); } template @@ -115,63 +69,23 @@ static void fill_2d_a(benchmark::State& state) { auto axes = std::make_tuple(reg(100, 0, 1), reg(100, 0, 1)); generator gen; auto storage = make_storage(axes); - for (auto _ : state) { - const auto i0 = std::get<0>(axes).index(gen()); - const auto i1 = std::get<1>(axes).index(gen()); - const auto stride = axis::traits::extent(std::get<0>(axes)); - ++storage[(i0 + 1) * stride + (i1 + 1)]; - } + for (auto _ : state) fill_a(axes, storage, std::forward_as_tuple(gen(), gen())); + state.SetItemsProcessed(state.iterations() * 2); } template -static void fill_2d_b(benchmark::State& state) { - auto axes = std::make_tuple(reg(100, 0, 1), reg(100, 0, 1)); - generator gen; - auto storage = make_storage(axes); - for (auto _ : state) { fill_b(axes, storage, std::forward_as_tuple(gen(), gen())); } -} - -template -static void fill_2d_c(benchmark::State& state) { - auto axes = std::make_tuple(reg(100, 0, 1), reg(100, 0, 1)); - generator gen; - auto storage = make_storage(axes); - auto strides = make_strides(axes); - BOOST_ASSERT(strides.size() == 3); - BOOST_ASSERT(strides[0] == 1); - BOOST_ASSERT(strides[1] == 102); - for (auto _ : state) { - fill_c(axes, strides.data(), storage, std::forward_as_tuple(gen(), gen())); - } -} - -template -static void fill_2d_c_dyn(benchmark::State& state) { +static void fill_2d_a_dyn(benchmark::State& state) { auto axes = vector_of_variant({reg(100, 0, 1), reg(100, 0, 1)}); generator gen; auto storage = make_storage(axes); - auto strides = make_strides(axes); - BOOST_ASSERT(strides.size() == 3); - BOOST_ASSERT(strides[0] == 1); - BOOST_ASSERT(strides[1] == 102); - for (auto _ : state) { - fill_c(axes, strides.data(), storage, std::forward_as_tuple(gen(), gen())); - } + for (auto _ : state) fill_a(axes, storage, std::forward_as_tuple(gen(), gen())); + state.SetItemsProcessed(state.iterations() * 2); } -BENCHMARK_TEMPLATE(fill_1d_a, int, uniform); BENCHMARK_TEMPLATE(fill_1d_a, double, uniform); -BENCHMARK_TEMPLATE(fill_1d_b, double, uniform); -BENCHMARK_TEMPLATE(fill_1d_c, double, uniform); -BENCHMARK_TEMPLATE(fill_1d_c_dyn, double, uniform); +BENCHMARK_TEMPLATE(fill_1d_a_dyn, double, uniform); BENCHMARK_TEMPLATE(fill_2d_a, double, uniform); -BENCHMARK_TEMPLATE(fill_2d_b, double, uniform); -BENCHMARK_TEMPLATE(fill_2d_c, double, uniform); -BENCHMARK_TEMPLATE(fill_2d_c_dyn, double, uniform); +BENCHMARK_TEMPLATE(fill_2d_a_dyn, double, uniform); BENCHMARK_TEMPLATE(fill_1d_a, double, normal); -BENCHMARK_TEMPLATE(fill_1d_b, double, normal); -BENCHMARK_TEMPLATE(fill_1d_c, double, normal); BENCHMARK_TEMPLATE(fill_2d_a, double, normal); -BENCHMARK_TEMPLATE(fill_2d_b, double, normal); -BENCHMARK_TEMPLATE(fill_2d_c, double, normal); diff --git a/benchmark/histogram_filling_gsl.cpp b/benchmark/histogram_filling_gsl.cpp index 132d6b24..94d73896 100644 --- a/benchmark/histogram_filling_gsl.cpp +++ b/benchmark/histogram_filling_gsl.cpp @@ -24,6 +24,7 @@ static void fill_1d(benchmark::State& state) { generator gen; for (auto _ : state) benchmark::DoNotOptimize(gsl_histogram_increment(h, gen())); gsl_histogram_free(h); + state.SetItemsProcessed(state.iterations()); } template @@ -34,6 +35,7 @@ static void fill_2d(benchmark::State& state) { for (auto _ : state) benchmark::DoNotOptimize(gsl_histogram2d_increment(h, gen(), gen())); gsl_histogram2d_free(h); + state.SetItemsProcessed(state.iterations() * 2); } BENCHMARK_TEMPLATE(fill_1d, uniform); diff --git a/benchmark/histogram_filling_root.cpp b/benchmark/histogram_filling_root.cpp index 05395376..98d63851 100644 --- a/benchmark/histogram_filling_root.cpp +++ b/benchmark/histogram_filling_root.cpp @@ -23,6 +23,7 @@ static void fill_1d(benchmark::State& state) { TH1I h("", "", 100, 0, 1); generator gen; for (auto _ : state) benchmark::DoNotOptimize(h.Fill(gen())); + state.SetItemsProcessed(state.iterations()); } template @@ -30,6 +31,7 @@ static void fill_2d(benchmark::State& state) { TH2I h("", "", 100, 0, 1, 100, 0, 1); generator gen; for (auto _ : state) benchmark::DoNotOptimize(h.Fill(gen(), gen())); + state.SetItemsProcessed(state.iterations() * 2); } template @@ -37,6 +39,7 @@ static void fill_3d(benchmark::State& state) { TH3I h("", "", 100, 0, 1, 100, 0, 1, 100, 0, 1); generator gen; for (auto _ : state) benchmark::DoNotOptimize(h.Fill(gen(), gen(), gen())); + state.SetItemsProcessed(state.iterations() * 3); } template @@ -50,6 +53,7 @@ static void fill_6d(benchmark::State& state) { const double buf[6] = {gen(), gen(), gen(), gen(), gen(), gen()}; benchmark::DoNotOptimize(h.Fill(buf)); } + state.SetItemsProcessed(state.iterations() * 6); } BENCHMARK_TEMPLATE(fill_1d, uniform); diff --git a/doc/Jamfile b/doc/Jamfile index a1ab2604..61278276 100644 --- a/doc/Jamfile +++ b/doc/Jamfile @@ -30,7 +30,7 @@ doxygen reference JAVADOC_AUTOBRIEF=YES EXCLUDE_SYMBOLS=detail "PREDEFINED=\"BOOST_HISTOGRAM_DOXYGEN_INVOKED\" \\ - \"BOOST_HISTOGRAM_NODISCARD\"" + \"BOOST_ATTRIBUTE_NODISCARD\"" ; make reference_pp.xml : reference.xml : @doxygen_postprocessing ; diff --git a/examples/guide_histogram_streaming.cpp b/examples/guide_histogram_streaming.cpp index a9f56469..eae31865 100644 --- a/examples/guide_histogram_streaming.cpp +++ b/examples/guide_histogram_streaming.cpp @@ -17,19 +17,8 @@ int main() { using namespace boost::histogram; namespace tr = axis::transform; - auto h = - make_histogram(axis::regular<>(2, -1.0, 1.0), - axis::regular(2, 1.0, 10.0, "axis 1"), - axis::regular( - tr::pow{1.5}, 2, 1.0, 10.0, "axis 2"), - // axis without metadata - axis::circular(4, 0.0, 360.0), - // axis without under-/overflow bins - axis::variable( - {-1.0, 0.0, 1.0}, "axis 4"), - axis::category<>({2, 1, 3}, "axis 5"), - axis::category({"red", "blue"}, "axis 6"), - axis::integer<>(-1, 1, "axis 7")); + auto h = make_histogram(axis::regular<>(2, -1.0, 1.0, "axis 1"), + axis::category({"red", "blue"}, "axis 2")); std::ostringstream os; os << h; @@ -38,14 +27,20 @@ int main() { assert(os.str() == "histogram(\n" - " regular(2, -1, 1, options=underflow | overflow),\n" - " regular_log(2, 1, 10, metadata=\"axis 1\", options=underflow | overflow),\n" - " regular_pow(2, 1, 10, metadata=\"axis 2\", options=growth, power=1.5),\n" - " regular(4, 0, 360, options=overflow | circular),\n" - " variable(-1, 0, 1, metadata=\"axis 4\", options=none),\n" - " category(2, 1, 3, metadata=\"axis 5\", options=overflow),\n" - " category(\"red\", \"blue\", metadata=\"axis 6\", options=overflow),\n" - " integer(-1, 1, metadata=\"axis 7\", options=underflow | overflow)\n" + " regular(2, -1, 1, metadata=\"axis 1\", options=underflow | overflow),\n" + " category(\"red\", \"blue\", metadata=\"axis 2\", options=overflow),\n" + " 0: 0\n" + " 1: 0\n" + " 2: 0\n" + " 3: 0\n" + " 4: 0\n" + " 5: 0\n" + " 6: 0\n" + " 7: 0\n" + " 8: 0\n" + " 9: 0\n" + " 10: 0\n" + " 11: 0\n" ")"); } diff --git a/include/boost/histogram/detail/args_type.hpp b/include/boost/histogram/detail/args_type.hpp index a6c442d5..1d5a1765 100644 --- a/include/boost/histogram/detail/args_type.hpp +++ b/include/boost/histogram/detail/args_type.hpp @@ -16,7 +16,8 @@ #if BOOST_WORKAROUND(BOOST_GCC, >= 60000) #pragma GCC diagnostic pop #endif -#include // mp_pop_front +#include // mp_pop_front +#include // mp_if #include #include // is_member_function_pointer @@ -24,9 +25,9 @@ namespace boost { namespace histogram { namespace detail { -template > -using args_type = std::conditional_t::value, - mp11::mp_pop_front, Args>; +template > +using args_type = + mp11::mp_if, mp11::mp_pop_front, Args>; template using arg_type = std::tuple_element_t>; diff --git a/include/boost/histogram/detail/at.hpp b/include/boost/histogram/detail/at.hpp new file mode 100644 index 00000000..ea5f7232 --- /dev/null +++ b/include/boost/histogram/detail/at.hpp @@ -0,0 +1,65 @@ +// Copyright 2015-2018 Hans Dembinski +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt +// or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_HISTOGRAM_DETAIL_AT_HPP +#define BOOST_HISTOGRAM_DETAIL_AT_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace histogram { +namespace detail { + +template +std::size_t linearize_index(optional_index& out, const std::size_t stride, const A& axis, + const axis::index_type i) { + // A may be axis or variant, cannot use static option detection here + const auto opt = axis::traits::options(axis); + const auto shift = opt & axis::option::underflow ? 1 : 0; + const auto extent = axis.size() + (opt & axis::option::overflow ? 1 : 0) + shift; + // i may be arbitrarily out of range + using namespace boost::mp11; + linearize(mp_false{}, mp_false{}, out, stride, extent, i + shift); + return extent; +} + +template +optional_index at(const A& axes, const std::tuple& args) noexcept { + optional_index idx{0}; + std::size_t stride = 1; + using namespace boost::mp11; + mp_for_each>([&](auto i) { + stride *= linearize_index(idx, stride, axis_get(axes), + static_cast(std::get(args))); + }); + return idx; +} + +template +optional_index at(const A& axes, const U& args) noexcept { + optional_index idx{0}; + std::size_t stride = 1; + using std::begin; + auto it = begin(args); + for_each_axis(axes, [&](const auto& a) { + stride *= linearize_index(idx, stride, a, static_cast(*it++)); + }); + return idx; +} + +} // namespace detail +} // namespace histogram +} // namespace boost + +#endif diff --git a/include/boost/histogram/detail/attribute.hpp b/include/boost/histogram/detail/attribute.hpp deleted file mode 100644 index 436b1149..00000000 --- a/include/boost/histogram/detail/attribute.hpp +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2018 Hans Dembinski -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt -// or copy at http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_HISTOGRAM_DETAIL_ATTRIBUTE_HPP -#define BOOST_HISTOGRAM_DETAIL_ATTRIBUTE_HPP - -#if __cplusplus >= 201703L -#define BOOST_HISTOGRAM_NODISCARD [[nodiscard]] -#else -#define BOOST_HISTOGRAM_NODISCARD -#endif - -#endif diff --git a/include/boost/histogram/detail/axes.hpp b/include/boost/histogram/detail/axes.hpp index 1372936d..cb4a1947 100644 --- a/include/boost/histogram/detail/axes.hpp +++ b/include/boost/histogram/detail/axes.hpp @@ -71,38 +71,39 @@ decltype(auto) axis_get(const T& axes) { } template -decltype(auto) axis_get(std::tuple& axes, unsigned i) { - using namespace boost::mp11; +decltype(auto) axis_get(std::tuple& axes, const unsigned i) { + using namespace ::boost::mp11; constexpr auto S = sizeof...(Ts); using V = mp_unique>; return mp_with_index(i, [&axes](auto i) { return V(&std::get(axes)); }); } template -decltype(auto) axis_get(const std::tuple& axes, unsigned i) { - using namespace boost::mp11; +decltype(auto) axis_get(const std::tuple& axes, const unsigned i) { + using namespace ::boost::mp11; constexpr auto S = sizeof...(Ts); using V = mp_unique>; return mp_with_index(i, [&axes](auto i) { return V(&std::get(axes)); }); } template -decltype(auto) axis_get(T& axes, unsigned i) { - return axes.at(i); +decltype(auto) axis_get(T& axes, const unsigned i) { + return axes[i]; } template -decltype(auto) axis_get(const T& axes, unsigned i) { - return axes.at(i); +decltype(auto) axis_get(const T& axes, const unsigned i) { + return axes[i]; } template bool axes_equal(const std::tuple& ts, const std::tuple& us) { - return static_if, mp11::mp_list>>( + using namespace ::boost::mp11; + return static_if, mp_list>>( [](const auto& ts, const auto& us) { - using N = mp11::mp_size>; + using N = mp_size>; bool equal = true; - mp11::mp_for_each>( + mp_for_each>( [&](auto I) { equal &= relaxed_equal(std::get(ts), std::get(us)); }); return equal; }, @@ -111,10 +112,10 @@ bool axes_equal(const std::tuple& ts, const std::tuple& us) { template bool axes_equal(const T& t, const std::tuple& u) { + using namespace ::boost::mp11; if (t.size() != sizeof...(Us)) return false; bool equal = true; - mp11::mp_for_each>( - [&](auto I) { equal &= t[I] == std::get(u); }); + mp_for_each>([&](auto I) { equal &= t[I] == std::get(u); }); return equal; } @@ -131,7 +132,8 @@ bool axes_equal(const T& t, const U& u) { template void axes_assign(std::tuple& t, const std::tuple& u) { - static_if, mp11::mp_list>>( + using namespace ::boost::mp11; + static_if, mp_list>>( [](auto& a, const auto& b) { a = b; }, [](auto&, const auto&) { BOOST_THROW_EXCEPTION( @@ -142,8 +144,9 @@ void axes_assign(std::tuple& t, const std::tuple& u) { template void axes_assign(std::tuple& t, const U& u) { - mp11::mp_for_each>([&](auto I) { - using T = mp11::mp_at_c, I>; + using namespace ::boost::mp11; + mp_for_each>([&](auto I) { + using T = mp_at_c, I>; std::get(t) = axis::get(u[I]); }); } @@ -152,8 +155,8 @@ template void axes_assign(T& t, const std::tuple& u) { // resize instead of reserve, because t may not be empty and we want exact capacity t.resize(sizeof...(Us)); - mp11::mp_for_each>( - [&](auto I) { t[I] = std::get(u); }); + using namespace ::boost::mp11; + mp_for_each>([&](auto I) { t[I] = std::get(u); }); } template @@ -175,30 +178,36 @@ auto make_empty_dynamic_axes(const std::tuple&) { return std::vector::value == 1), mp_first, L>>{}; } -template +template void axis_index_is_valid(const T& axes, const unsigned N) { BOOST_ASSERT_MSG(N < axes_rank(axes), "index out of range"); } -template -void for_each_axis_impl(std::true_type, const T& axes, F&& f) { - for (const auto& x : axes) { axis::visit(std::forward(f), x); } +template +void for_each_axis_impl(std::true_type, Axes&& axes, V&& v) { + for (auto&& a : axes) { axis::visit(std::forward(v), a); } } -template -void for_each_axis_impl(std::false_type, const T& axes, F&& f) { - for (const auto& x : axes) std::forward(f)(x); +template +void for_each_axis_impl(std::false_type, Axes&& axes, V&& v) { + for (auto&& a : axes) std::forward(v)(a); } -template -void for_each_axis(const T& axes, F&& f) { - using U = mp11::mp_first; - for_each_axis_impl(is_axis_variant(), axes, std::forward(f)); +template +void for_each_axis(Axes&& a, V&& v) { + using namespace ::boost::mp11; + using T = mp_first>; + for_each_axis_impl(is_axis_variant(), std::forward(a), std::forward(v)); } -template -void for_each_axis(const std::tuple& axes, F&& f) { - mp11::tuple_for_each(axes, std::forward(f)); +template +void for_each_axis(const std::tuple& a, V&& v) { + mp11::tuple_for_each(a, std::forward(v)); +} + +template +void for_each_axis(std::tuple& a, V&& v) { + mp11::tuple_for_each(a, std::forward(v)); } template @@ -214,12 +223,12 @@ std::size_t bincount(const T& axes) { } template -using tuple_size_t = typename std::tuple_size::type; +using buffer_size_impl = typename std::tuple_size::type; template using buffer_size = mp11::mp_eval_or< - std::integral_constant, tuple_size_t, - T>; + std::integral_constant, + buffer_size_impl, T>; template class sub_array : public std::array { @@ -257,6 +266,49 @@ auto make_stack_buffer(const T& t, const Ts&... ts) { return stack_buffer(axes_rank(t), ts...); } +template +using has_underflow = + decltype(axis::traits::static_options::test(axis::option::underflow)); + +template +constexpr auto inclusive_options(Axis&&) { + return axis::option::underflow | axis::option::overflow; +} + +template +constexpr auto inclusive_options(axis::category&&) { + return axis::option::overflow; +} + +template +using is_non_inclusive = mp11::mp_not::test(inclusive_options(std::declval())))>; + +template +using is_growing = decltype(axis::traits::static_options::test(axis::option::growth)); + +template +using is_multidim = is_tuple>>; + +template