faster indexed iteration, replaced boost::container::X with std::X

This commit is contained in:
Hans Dembinski 2019-01-27 19:46:46 +01:00
parent 451f36c774
commit 0bb5c1b3f4
17 changed files with 1811 additions and 9046 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 78 KiB

View File

@ -28,7 +28,7 @@ for iaxis, axis_type in enumerate(("tuple", "vector", "vector_of_variant")):
for (name, axis_t, dim, cov), v in bench.items():
if axis_t != axis_type: continue
if cov != "inner": continue
v = np.sort(v).T
v = np.sort(v, axis=0).T
# if "semi_dynamic" in axis: continue
name2, col, ls = {
"Naive": ("nested for", "r", "--"),

View File

@ -10,7 +10,6 @@
#include <algorithm>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/container/new_allocator.hpp>
#include <boost/core/ignore_unused.hpp>
#include <boost/cstdint.hpp>
#include <boost/histogram/detail/buffer.hpp>

View File

@ -8,9 +8,6 @@
#define BOOST_HISTOGRAM_AXIS_CATEGORY_HPP
#include <algorithm>
#include <boost/container/new_allocator.hpp>
#include <boost/container/string.hpp> // default meta data
#include <boost/container/vector.hpp>
#include <boost/histogram/axis/iterator.hpp>
#include <boost/histogram/detail/compressed_pair.hpp>
#include <boost/histogram/detail/meta.hpp>
@ -19,6 +16,7 @@
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <vector>
namespace boost {
namespace histogram {
@ -66,7 +64,7 @@ class category
using metadata_type = MetaData;
using value_type = Value;
using allocator_type = Allocator;
using vector_type = boost::container::vector<value_type, allocator_type>;
using vector_type = std::vector<value_type, allocator_type>;
public:
category() = default;
@ -106,6 +104,14 @@ public:
allocator_type alloc = {})
: category(list.begin(), list.end(), std::move(meta), std::move(alloc)) {}
/// Constructor used by algorithm::reduce to shrink and rebin.
category(const category& src, index_type begin, index_type end, unsigned merge)
: category(src.vec_meta_.first().begin() + begin,
src.vec_meta_.first().begin() + end, src.metadata()) {
if (merge > 1)
BOOST_THROW_EXCEPTION(std::invalid_argument("cannot merge bins for category axis"));
}
/// Return index for value argument.
index_type operator()(const value_type& x) const noexcept {
const auto beg = vec_meta_.first().begin();
@ -157,7 +163,7 @@ private:
template <class T>
category(std::initializer_list<T>)->category<T>;
category(std::initializer_list<const char*>)->category<boost::container::string>;
category(std::initializer_list<const char*>)->category<std::string>;
template <class T>
category(std::initializer_list<T>, const char*)->category<T>;

View File

@ -7,7 +7,6 @@
#ifndef BOOST_HISTOGRAM_AXIS_INTEGER_HPP
#define BOOST_HISTOGRAM_AXIS_INTEGER_HPP
#include <boost/container/string.hpp> // default meta data
#include <boost/histogram/axis/iterator.hpp>
#include <boost/histogram/detail/compressed_pair.hpp>
#include <boost/histogram/detail/meta.hpp>

View File

@ -7,7 +7,7 @@
#ifndef BOOST_HISTOGRAM_AXIS_REGULAR_HPP
#define BOOST_HISTOGRAM_AXIS_REGULAR_HPP
#include <boost/container/string.hpp> // default meta data
#include <boost/assert.hpp>
#include <boost/histogram/axis/interval_view.hpp>
#include <boost/histogram/axis/iterator.hpp>
#include <boost/histogram/detail/compressed_pair.hpp>

View File

@ -8,9 +8,7 @@
#define BOOST_HISTOGRAM_AXIS_VARIABLE_HPP
#include <algorithm>
#include <boost/container/new_allocator.hpp>
#include <boost/container/string.hpp> // default meta data
#include <boost/container/vector.hpp>
#include <boost/assert.hpp>
#include <boost/histogram/axis/interval_view.hpp>
#include <boost/histogram/axis/iterator.hpp>
#include <boost/histogram/detail/compressed_pair.hpp>
@ -23,6 +21,7 @@
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <vector>
namespace boost {
namespace histogram {
@ -79,7 +78,7 @@ class variable
using metadata_type = MetaData;
using value_type = Value;
using allocator_type = Allocator;
using vec_type = boost::container::vector<Value, allocator_type>;
using vec_type = std::vector<Value, allocator_type>;
public:
variable() = default;

View File

@ -100,15 +100,15 @@ void linearize_index(optional_index& out, const T& axis, const int j) {
template <class S, class A, class T>
void maybe_replace_storage(S& storage, const A& axes, const T& shifts) {
bool update_needed = false;
for (int s : shifts) update_needed |= s != 0;
auto sh = shifts;
for_each_axis(axes, [&](const auto&) { update_needed |= *sh++; });
if (!update_needed) return;
struct item {
int idx, old_extend;
std::size_t new_stride;
};
auto data = make_stack_buffer<item>(axes);
auto sit = shifts.begin();
auto dit = data.begin();
} data[buffer_size<A>::value];
auto sit = shifts;
auto dit = data;
std::size_t s = 1;
for_each_axis(axes, [&](const auto& a) {
const auto n = axis::traits::extend(a);
@ -119,8 +119,8 @@ void maybe_replace_storage(S& storage, const A& axes, const T& shifts) {
new_storage.reset(detail::bincount(axes));
for (const auto& x : storage) {
auto ns = new_storage.begin();
sit = shifts.begin();
dit = data.begin();
sit = shifts;
dit = data;
for_each_axis(axes, [&](const auto& a) {
if (axis::test(axis::traits::options(a), axis::option::underflow)) {
if (dit->idx == 0) {
@ -143,10 +143,9 @@ void maybe_replace_storage(S& storage, const A& axes, const T& shifts) {
++sit;
});
*ns = x;
dit = data.begin();
const auto last = data.end() - 1;
dit = data;
++dit->idx;
while (dit != last && dit->idx == dit->old_extend) {
while (dit != (data + get_size(axes) - 1) && dit->idx == dit->old_extend) {
dit->idx = 0;
++(++dit)->idx;
}
@ -189,7 +188,7 @@ optional_index args_to_index(std::false_type, S&, T& axes, const U& args) {
template <unsigned I, unsigned N, class S, class T, class U>
optional_index args_to_index(std::true_type, S& storage, T& axes, const U& args) {
optional_index idx;
auto shifts = make_stack_buffer<int>(axes);
int shifts[buffer_size<T>::value];
const auto rank = get_size(axes);
if (rank == 1 && N > 1)
linearize_value(idx, shifts[0], axis_get<0>(axes), tuple_slice<I, N>(args));

View File

@ -29,7 +29,7 @@
#if BOOST_WORKAROUND(BOOST_GCC, >= 60000)
#pragma GCC diagnostic pop
#endif
#include <boost/container/static_vector.hpp>
#include <array>
#include <boost/histogram/fwd.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/function.hpp>
@ -323,13 +323,31 @@ std::size_t get_size(const T& t) noexcept {
return get_size_impl(mp11::mp_valid<tuple_size_t, T>(), t);
}
template <class T>
using buffer_size =
mp_eval_or<tuple_size_t, T,
std::integral_constant<std::size_t, BOOST_HISTOGRAM_DETAIL_AXES_LIMIT>>;
template <class T, std::size_t N>
class subarray : public std::array<T, N> {
public:
explicit subarray(std::size_t s) : size_(s) {}
subarray(std::size_t s, T value) : size_(s) { std::array<T, N>::fill(value); }
auto end() noexcept { return std::array<T, N>::begin() + size_; }
auto end() const noexcept { return std::array<T, N>::begin() + size_; }
auto size() const noexcept { return size_; }
private:
std::size_t size_ = N;
};
template <class U, class T>
using stack_buffer = boost::container::static_vector<
U, mp_eval_or<tuple_size_t, T,
mp11::mp_size_t<BOOST_HISTOGRAM_DETAIL_AXES_LIMIT>>::value>;
using stack_buffer = subarray<U, buffer_size<T>::value>;
template <class U, class T, class... Ts>
auto make_stack_buffer(const T& t, Ts... ts) {
auto make_stack_buffer(const T& t, Ts&&... ts) {
return stack_buffer<U, T>(get_size(t), std::forward<Ts>(ts)...);
}

View File

@ -12,12 +12,9 @@
Forward declarations, basic typedefs, and default template arguments for main classes.
*/
#include <boost/container/container_fwd.hpp> // string, vector, new_allocator
#include <boost/histogram/attribute.hpp> // BOOST_HISTOGRAM_NODISCARD
// Why boost containers and not std containers?
// - boost has the static_vector specialization, which is internally used a lot
// - std::string is very large on MSVC, boost::container::string is small everywhere
#include <boost/histogram/attribute.hpp> // BOOST_HISTOGRAM_NODISCARD
#include <string>
#include <vector>
namespace boost {
namespace histogram {
@ -34,7 +31,7 @@ using real_index_type = double;
struct null_type {};
/// default metadata type
using default_metadata = boost::container::string;
using default_metadata = std::string;
enum class option {
none = 0,
@ -89,13 +86,11 @@ template <class Value = int, class MetaData = default_metadata,
class integer;
template <class Value = double, class MetaData = default_metadata,
option Options = option::use_default,
class Allocator = boost::container::new_allocator<Value>>
option Options = option::use_default, class Allocator = std::allocator<Value>>
class variable;
template <class Value = int, class MetaData = default_metadata,
option Options = option::overflow,
class Allocator = boost::container::new_allocator<Value>>
option Options = option::overflow, class Allocator = std::allocator<Value>>
class category;
template <class... Ts>
@ -121,14 +116,14 @@ class weighted_mean;
struct unsafe_access;
template <class Allocator = boost::container::new_allocator<void>>
template <class Allocator = std::allocator<void>>
class adaptive_storage;
template <class T>
class storage_adaptor;
template <class T, class A = boost::container::new_allocator<T>>
using dense_storage = storage_adaptor<boost::container::vector<T, A>>;
template <class T, class A = std::allocator<T>>
using dense_storage = storage_adaptor<std::vector<T, A>>;
using default_storage = adaptive_storage<>;

View File

@ -30,38 +30,36 @@ enum class coverage {
/// Range over histogram bins with multi-dimensional index.
template <class Histogram>
class BOOST_HISTOGRAM_NODISCARD indexed_range {
using max_dim = mp11::mp_size_t<
detail::buffer_size<typename detail::naked<Histogram>::axes_type>::value>;
using value_iterator = decltype(std::declval<Histogram>().begin());
struct cache_item {
int idx, begin, end, extend;
};
using cache_type =
detail::stack_buffer<cache_item, typename detail::naked<Histogram>::axes_type>;
public:
/// Pointer-like class to access value and index of current cell.
class accessor {
public:
/// Array-like view into the current multi-dimensional index.
class indices_view {
class index_view {
public:
class index_iterator
: public boost::iterator_adaptor<index_iterator,
typename cache_type::const_iterator> {
: public boost::iterator_adaptor<index_iterator, const cache_item*> {
public:
index_iterator(typename cache_type::const_iterator i)
: index_iterator::iterator_adaptor_(i) {}
index_iterator(const cache_item* i) : index_iterator::iterator_adaptor_(i) {}
decltype(auto) operator*() const noexcept { return index_iterator::base()->idx; }
};
auto begin() const noexcept { return index_iterator(cache_.begin()); }
auto end() const noexcept { return index_iterator(cache_.end()); }
auto size() const noexcept { return cache_.size(); }
int operator[](unsigned d) const noexcept { return cache_[d].idx; }
int at(unsigned d) const { return cache_.at(d).idx; }
auto begin() const noexcept { return index_iterator(begin_); }
auto end() const noexcept { return index_iterator(end_); }
auto size() const noexcept { return static_cast<std::size_t>(end_ - begin_); }
int operator[](unsigned d) const noexcept { return begin_[d].idx; }
int at(unsigned d) const { return begin_[d].idx; }
private:
indices_view(const cache_type& c) : cache_(c) {}
const cache_type& cache_;
index_view(const cache_item* b, const cache_item* e) : begin_(b), end_(e) {}
const cache_item *begin_, *end_;
friend class accessor;
};
@ -72,7 +70,9 @@ public:
// convenience interface
int index(unsigned d = 0) const noexcept { return parent_.cache_[d].idx; }
auto indices() const noexcept { return indices_view(parent_.cache_); }
auto indices() const noexcept {
return index_view(parent_.cache_, parent_.cache_ + parent_.hist_.rank());
}
template <unsigned N = 0>
decltype(auto) bin(std::integral_constant<unsigned, N> = {}) const {
@ -83,7 +83,7 @@ public:
double density() const {
double x = 1;
auto it = parent_->cache_.begin();
auto it = parent_.cache_;
parent_.hist_.for_each_axis([&](const auto& a) {
const auto w = axis::traits::width_as<double>(a, it++->idx);
x *= w ? w : 1;
@ -110,10 +110,10 @@ public:
void increment() noexcept {
std::size_t stride = 1;
auto c = parent_->cache_.begin();
auto c = parent_->cache_;
++c->idx;
++range_iterator::base_reference();
while (c->idx == c->end && ((c + 1) != parent_->cache_.end())) {
while (c->idx == c->end && (c != (parent_->cache_ + parent_->hist_.rank() - 1))) {
c->idx = c->begin;
range_iterator::base_reference() -= (c->end - c->begin) * stride;
stride *= c->extend;
@ -130,12 +130,9 @@ public:
};
indexed_range(Histogram& h, coverage c)
: hist_(h)
, cover_all_(c == coverage::all)
, begin_(hist_.begin())
, end_(begin_)
, cache_(hist_.rank()) {
auto ca = cache_.begin();
: hist_(h), cover_all_(c == coverage::all), begin_(hist_.begin()), end_(begin_) {
auto ca = cache_;
const auto clast = ca + hist_.rank() - 1;
std::size_t stride = 1;
h.for_each_axis([&, this](const auto& a) {
const auto opt = axis::traits::options(a);
@ -147,7 +144,7 @@ public:
ca->idx = ca->begin;
begin_ += (ca->begin + shift) * stride;
if ((ca + 1) < cache_.end())
if (ca < clast)
end_ += (ca->begin + shift) * stride;
else
end_ += (ca->end + shift) * stride;
@ -164,7 +161,7 @@ private:
Histogram& hist_;
const bool cover_all_;
value_iterator begin_, end_;
mutable cache_type cache_;
mutable cache_item cache_[max_dim::value];
};
/**

View File

@ -12,7 +12,6 @@
Collection of factory functions to conveniently create histograms.
*/
#include <boost/container/vector.hpp>
#include <boost/histogram/accumulators/weighted_sum.hpp>
#include <boost/histogram/adaptive_storage.hpp> // implements default_storage
#include <boost/histogram/detail/meta.hpp>
@ -20,6 +19,7 @@
#include <boost/histogram/storage_adaptor.hpp>
#include <boost/mp11/utility.hpp>
#include <tuple>
#include <vector>
namespace boost {
namespace histogram {
@ -73,7 +73,7 @@ auto make_histogram_with(Storage&& storage, Iterable&& iterable) {
using S = mp11::mp_if<detail::is_storage<U>, U, storage_adaptor<U>>;
using It = detail::naked<Iterable>;
using A = mp11::mp_if<detail::is_indexable_container<It>, It,
boost::container::vector<mp11::mp_first<It>>>;
std::vector<mp11::mp_first<It>>>;
return histogram<A, S>(std::forward<Iterable>(iterable),
S(std::forward<Storage>(storage)));
}
@ -106,8 +106,7 @@ template <typename Storage, typename Iterator,
typename = detail::requires_iterator<Iterator>>
auto make_histogram_with(Storage&& storage, Iterator begin, Iterator end) {
using T = detail::naked<decltype(*begin)>;
return make_histogram_with(std::forward<Storage>(storage),
boost::container::vector<T>(begin, end));
return make_histogram_with(std::forward<Storage>(storage), std::vector<T>(begin, end));
}
/**

View File

@ -7,8 +7,6 @@
#ifndef BOOST_HISTOGRAM_SERIALIZATION_HPP
#define BOOST_HISTOGRAM_SERIALIZATION_HPP
#include <boost/container/string.hpp>
#include <boost/container/vector.hpp>
#include <boost/histogram/accumulators/mean.hpp>
#include <boost/histogram/accumulators/sum.hpp>
#include <boost/histogram/accumulators/weighted_mean.hpp>
@ -49,32 +47,6 @@ void serialize(Archive& ar, tuple<Ts...>& t, unsigned /* version */) {
} // namespace std
namespace boost {
namespace container {
template <class Archive, class T, class A>
void serialize(Archive& ar, vector<T, A>& v, unsigned) {
std::size_t size = v.size();
ar& size;
if (Archive::is_loading::value) { v.resize(size); }
if (std::is_trivially_copyable<T>::value) {
ar& ::boost::serialization::make_array(v.data(), size);
} else {
for (auto&& x : v) ar& x;
}
}
template <class Archive, class C, class T, class A>
void serialize(Archive& ar, basic_string<C, T, A>& v, unsigned) {
std::size_t size = v.size();
ar& size;
if (Archive::is_loading::value) v.resize(size);
if (std::is_trivially_copyable<T>::value) {
ar& ::boost::serialization::make_array(v.data(), size);
} else {
for (auto&& x : v) ar& x;
}
}
} // namespace container
namespace histogram {
namespace accumulators {

View File

@ -92,8 +92,7 @@ int main() {
axis::category d({1, 2}, axis::null_type{});
BOOST_TEST_TRAIT_TRUE((std::is_same<decltype(a), axis::category<int>>));
BOOST_TEST_TRAIT_TRUE(
(std::is_same<decltype(b), axis::category<boost::container::string>>));
BOOST_TEST_TRAIT_TRUE((std::is_same<decltype(b), axis::category<std::string>>));
BOOST_TEST_TRAIT_TRUE((std::is_same<decltype(c), axis::category<int>>));
BOOST_TEST_TRAIT_TRUE(
(std::is_same<decltype(d), axis::category<int, axis::null_type>>));

View File

@ -20,14 +20,14 @@ void run_tests() {
// histogram_serialization
{
namespace tr = axis::transform;
auto a = make(
Tag(), axis::regular<>(3, -1, 1, "axis 0"),
axis::circular<>(4, 0.0, 1.0, "axis 1"),
axis::regular<double, tr::log>(3, 1, 100, "axis 2"),
axis::regular<double, tr::pow, boost::container::string,
axis::option::overflow>(tr::pow(0.5), 3, 1, 100, "axis 3"),
axis::variable<>({0.1, 0.2, 0.3, 0.4, 0.5}, "axis 4"), axis::category<>{3, 1, 2},
axis::integer<int, axis::null_type>(0, 2));
auto a =
make(Tag(), axis::regular<>(3, -1, 1, "axis 0"),
axis::circular<>(4, 0.0, 1.0, "axis 1"),
axis::regular<double, tr::log>(3, 1, 100, "axis 2"),
axis::regular<double, tr::pow, std::vector<int>, axis::option::overflow>(
tr::pow(0.5), 3, 1, 100, {1, 2, 3}),
axis::variable<>({0.1, 0.2, 0.3, 0.4, 0.5}, "axis 4"),
axis::category<>{3, 1, 2}, axis::integer<int, axis::null_type>(0, 2));
a(0.5, 0.2, 20, 20, 0.25, 1, 1);
std::string buf;
{

View File

@ -127,26 +127,26 @@ static void Indexed(benchmark::State& state, Tag, d3, coverage cov) {
BENCH(Naive, tuple, 1, inner);
BENCH(Indexed, tuple, 1, inner);
// BENCH(Naive, vector, 1, inner);
// BENCH(Indexed, vector, 1, inner);
//
// BENCH(Naive, vector_of_variant, 1, inner);
// BENCH(Indexed, vector_of_variant, 1, inner);
BENCH(Naive, vector, 1, inner);
BENCH(Indexed, vector, 1, inner);
BENCH(Naive, vector_of_variant, 1, inner);
BENCH(Indexed, vector_of_variant, 1, inner);
BENCH(Naive, tuple, 2, inner);
BENCH(Indexed, tuple, 2, inner);
// BENCH(Naive, vector, 2, inner);
// BENCH(Indexed, vector, 2, inner);
//
// BENCH(Naive, vector_of_variant, 2, inner);
// BENCH(Indexed, vector_of_variant, 2, inner);
//
// BENCH(Naive, tuple, 3, inner);
// BENCH(Indexed, tuple, 3, inner);
//
// BENCH(Naive, vector, 3, inner);
// BENCH(Indexed, vector, 3, inner);
//
// BENCH(Naive, vector_of_variant, 3, inner);
// BENCH(Indexed, vector_of_variant, 3, inner);
BENCH(Naive, vector, 2, inner);
BENCH(Indexed, vector, 2, inner);
BENCH(Naive, vector_of_variant, 2, inner);
BENCH(Indexed, vector_of_variant, 2, inner);
BENCH(Naive, tuple, 3, inner);
BENCH(Indexed, tuple, 3, inner);
BENCH(Naive, vector, 3, inner);
BENCH(Indexed, vector, 3, inner);
BENCH(Naive, vector_of_variant, 3, inner);
BENCH(Indexed, vector_of_variant, 3, inner);