mirror of
https://github.com/boostorg/histogram.git
synced 2025-05-11 21:24:14 +00:00
make axis::variable allocator aware
This commit is contained in:
parent
8216af927e
commit
f1c59deb04
@ -11,7 +11,7 @@ int main() {
|
||||
v.push_back(bh::axis::regular<>(100, -1, 1));
|
||||
v.push_back(bh::axis::integer<>(1, 7));
|
||||
|
||||
// create dynamic histogram (make_static_histogram be used with iterators)
|
||||
// create dynamic histogram (make_static_histogram cannot be used with iterators)
|
||||
auto h = bh::make_dynamic_histogram(v.begin(), v.end());
|
||||
|
||||
// do something with h
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
// forward declaration for serialization
|
||||
namespace boost {
|
||||
@ -118,6 +119,7 @@ public:
|
||||
using allocator_type = typename base_type::allocator_type;
|
||||
using value_type = RealType;
|
||||
using bin_type = interval_view<regular>;
|
||||
using transform_type = Transform;
|
||||
|
||||
/** Construct axis with n bins over real range [lower, upper).
|
||||
*
|
||||
@ -129,10 +131,10 @@ public:
|
||||
* \param trans arguments passed to the transform.
|
||||
*/
|
||||
regular(unsigned n, value_type lower, value_type upper, string_view label = {},
|
||||
uoflow_type uo = uoflow_type::on, Transform trans = Transform(),
|
||||
uoflow_type uo = uoflow_type::on, transform_type trans = transform_type(),
|
||||
const allocator_type& a = allocator_type())
|
||||
: base_type(n, uo, label, a)
|
||||
, Transform(trans)
|
||||
, transform_type(trans)
|
||||
, min_(trans.forward(lower))
|
||||
, delta_((trans.forward(upper) - trans.forward(lower)) / n) {
|
||||
if (lower < upper) {
|
||||
@ -152,7 +154,7 @@ public:
|
||||
/// Returns the bin index for the passed argument.
|
||||
int index(value_type x) const noexcept {
|
||||
// Optimized code, measure impact of changes
|
||||
const value_type z = (Transform::forward(x) - min_) / delta_;
|
||||
const value_type z = (transform_type::forward(x) - min_) / delta_;
|
||||
return z < base_type::size() ? (z >= 0.0 ? static_cast<int>(z) : -1)
|
||||
: base_type::size();
|
||||
}
|
||||
@ -169,19 +171,19 @@ public:
|
||||
const auto z = value_type(i) / n;
|
||||
x = (1.0 - z) * min_ + z * (min_ + delta_ * n);
|
||||
}
|
||||
return Transform::inverse(x);
|
||||
return transform_type::inverse(x);
|
||||
}
|
||||
|
||||
bin_type operator[](int idx) const noexcept { return bin_type(idx, *this); }
|
||||
|
||||
bool operator==(const regular& o) const noexcept {
|
||||
return base_type::operator==(o) && Transform::operator==(o) && min_ == o.min_ &&
|
||||
return base_type::operator==(o) && transform_type::operator==(o) && min_ == o.min_ &&
|
||||
delta_ == o.delta_;
|
||||
}
|
||||
|
||||
/// Access properties of the transform.
|
||||
const Transform& transform() const noexcept {
|
||||
return static_cast<const Transform&>(*this);
|
||||
const transform_type& transform() const noexcept {
|
||||
return static_cast<const transform_type&>(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -273,6 +275,10 @@ public:
|
||||
using value_type = RealType;
|
||||
using bin_type = interval_view<variable>;
|
||||
|
||||
private:
|
||||
using value_allocator_type = typename std::allocator_traits<allocator_type>::template rebind_alloc<value_type>;
|
||||
public:
|
||||
|
||||
/** Construct an axis from bin edges.
|
||||
*
|
||||
* \param x sequence of bin edges.
|
||||
@ -281,42 +287,90 @@ public:
|
||||
*/
|
||||
variable(std::initializer_list<value_type> x, string_view label = {},
|
||||
uoflow_type uo = uoflow_type::on, const allocator_type& a = allocator_type())
|
||||
: base_type(x.size() - 1, uo, label, a), x_(new value_type[x.size()]) {
|
||||
if (x.size() >= 2) {
|
||||
std::copy(x.begin(), x.end(), x_.get());
|
||||
std::sort(x_.get(), x_.get() + base_type::size() + 1);
|
||||
} else {
|
||||
throw std::invalid_argument("at least two values required");
|
||||
}
|
||||
}
|
||||
: variable(x.begin(), x.end(), label, uo, a)
|
||||
{}
|
||||
|
||||
template <typename Iterator>
|
||||
variable(Iterator begin, Iterator end, string_view label = {},
|
||||
uoflow_type uo = uoflow_type::on, const allocator_type& a = allocator_type())
|
||||
: base_type(std::distance(begin, end) - 1, uo, label, a)
|
||||
, x_(new value_type[std::distance(begin, end)]) {
|
||||
std::copy(begin, end, x_.get());
|
||||
std::sort(x_.get(), x_.get() + base_type::size() + 1);
|
||||
: base_type(std::max(std::distance(begin, end), 1l) - 1, uo, label, a) {
|
||||
value_allocator_type a2(a);
|
||||
using AT = std::allocator_traits<value_allocator_type>;
|
||||
x_ = AT::allocate(a2, base_type::size() + 1);
|
||||
auto xit = x_;
|
||||
AT::construct(a2, xit, *begin);
|
||||
++begin;
|
||||
while (begin != end) {
|
||||
if (*begin <= *xit)
|
||||
throw std::invalid_argument("input sequence must be strictly ascending");
|
||||
AT::construct(a2, ++xit, *begin++);
|
||||
}
|
||||
}
|
||||
|
||||
variable() = default;
|
||||
variable(const variable& o) : base_type(o), x_(new value_type[base_type::size() + 1]) {
|
||||
std::copy(o.x_.get(), o.x_.get() + base_type::size() + 1, x_.get());
|
||||
|
||||
variable(const variable& o) : base_type(o) {
|
||||
value_allocator_type a(o.get_allocator());
|
||||
using AT = std::allocator_traits<value_allocator_type>;
|
||||
x_ = AT::allocate(a, base_type::size() + 1);
|
||||
auto it = o.x_;
|
||||
const auto end = o.x_ + base_type::size() + 1;
|
||||
auto xit = x_;
|
||||
for (; it != end; ++it, ++xit)
|
||||
AT::construct(a, xit, *it);
|
||||
}
|
||||
|
||||
variable& operator=(const variable& o) {
|
||||
if (this != &o) {
|
||||
base::operator=(o);
|
||||
x_.reset(new value_type[base_type::size() + 1]);
|
||||
std::copy(o.x_.get(), o.x_.get() + base_type::size() + 1, x_.get());
|
||||
if (base_type::size() != o.size()) {
|
||||
using AT = std::allocator_traits<value_allocator_type>;
|
||||
value_allocator_type old_alloc(base_type::get_allocator());
|
||||
auto xit = x_;
|
||||
const auto xend = xit + base_type::size() + 1;
|
||||
while (xit != xend)
|
||||
AT::destroy(old_alloc, xit++);
|
||||
AT::deallocate(old_alloc, x_, base_type::size() + 1);
|
||||
base::operator=(o);
|
||||
value_allocator_type new_alloc(base_type::get_allocator());
|
||||
x_ = AT::allocate(new_alloc, o.size() + 1);
|
||||
xit = x_;
|
||||
auto it = o.x_;
|
||||
const auto end = o.x_ + o.size() + 1;
|
||||
while (it != end)
|
||||
AT::construct(new_alloc, xit++, *it++);
|
||||
} else {
|
||||
base::operator=(o);
|
||||
std::copy(o.x_, o.x_ + o.size() + 1, x_);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
variable(variable&&) = default;
|
||||
variable& operator=(variable&&) = default;
|
||||
|
||||
variable(variable&& o) : base_type(std::move(o)) {
|
||||
x_ = o.x_;
|
||||
o.x_ = nullptr;
|
||||
}
|
||||
|
||||
variable& operator=(variable&& o) {
|
||||
base::operator=(std::move(o));
|
||||
x_ = o.x_;
|
||||
o.x_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~variable() {
|
||||
value_allocator_type a(base_type::get_allocator());
|
||||
using AT = std::allocator_traits<value_allocator_type>;
|
||||
auto xit = x_;
|
||||
const auto end = x_ + base_type::size() + 1;
|
||||
while (xit != end)
|
||||
AT::destroy(a, xit++);
|
||||
AT::deallocate(a, x_, base_type::size() + 1);
|
||||
}
|
||||
|
||||
/// Returns the bin index for the passed argument.
|
||||
int index(value_type x) const noexcept {
|
||||
return std::upper_bound(x_.get(), x_.get() + base_type::size() + 1, x) - x_.get() - 1;
|
||||
return std::upper_bound(x_, x_ + base_type::size() + 1, x) - x_ - 1;
|
||||
}
|
||||
|
||||
/// Returns the starting edge of the bin.
|
||||
@ -330,11 +384,11 @@ public:
|
||||
|
||||
bool operator==(const variable& o) const noexcept {
|
||||
if (!base::operator==(o)) { return false; }
|
||||
return std::equal(x_.get(), x_.get() + base_type::size() + 1, o.x_.get());
|
||||
return std::equal(x_, x_ + base_type::size() + 1, o.x_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<value_type[]> x_; // smaller size compared to std::vector
|
||||
value_type* x_ = nullptr;
|
||||
|
||||
friend class ::boost::serialization::access;
|
||||
template <class Archive>
|
||||
|
@ -37,7 +37,7 @@ struct axes_equal_static_dynamic_impl {
|
||||
template <typename Int>
|
||||
void operator()(Int) const {
|
||||
using T = mp11::mp_at<StaticAxes, Int>;
|
||||
auto tp = boost::get<T>(&v[Int::value]);
|
||||
auto tp = boost::relaxed_get<T>(&v[Int::value]);
|
||||
equal &= (tp && *tp == std::get<Int::value>(t));
|
||||
}
|
||||
};
|
||||
|
@ -270,10 +270,9 @@ private:
|
||||
template <typename Storage, typename... Ts>
|
||||
histogram<static_axes<detail::rm_cv_ref<Ts>...>, detail::rm_cv_ref<Storage>>
|
||||
make_static_histogram_with(Storage&& s, Ts&&... axis) {
|
||||
using histogram_type =
|
||||
histogram<static_axes<detail::rm_cv_ref<Ts>...>, detail::rm_cv_ref<Storage>>;
|
||||
auto axes = typename histogram_type::axes_type(std::forward<Ts>(axis)...);
|
||||
return histogram_type(std::move(axes), std::move(s));
|
||||
using H = histogram<static_axes<detail::rm_cv_ref<Ts>...>, detail::rm_cv_ref<Storage>>;
|
||||
auto axes = typename H::axes_type(std::forward<Ts>(axis)...);
|
||||
return H(std::move(axes), std::forward<Storage>(s));
|
||||
}
|
||||
|
||||
/// static type factory with standard storage type
|
||||
@ -287,13 +286,13 @@ histogram<static_axes<detail::rm_cv_ref<Ts>...>> make_static_histogram(Ts&&... a
|
||||
template <typename AnyAxisType=axis::any_std, typename Storage, typename T, typename... Ts>
|
||||
histogram<mp11::mp_rename<AnyAxisType, dynamic_axes>, detail::rm_cv_ref<Storage>>
|
||||
make_dynamic_histogram_with(Storage&& s, T&& axis0, Ts&&... axis) {
|
||||
using histogram_type = histogram<
|
||||
using H = histogram<
|
||||
mp11::mp_rename<AnyAxisType, dynamic_axes>, detail::rm_cv_ref<Storage>
|
||||
>;
|
||||
auto a = axis0.get_allocator();
|
||||
auto axes = typename histogram_type::axes_type(
|
||||
auto axes = typename H::axes_type(
|
||||
{AnyAxisType(std::forward<T>(axis0)), AnyAxisType(std::forward<Ts>(axis))...}, a);
|
||||
return histogram_type(std::move(axes), std::move(s));
|
||||
return H(std::move(axes), std::forward<Storage>(s));
|
||||
}
|
||||
|
||||
/// dynamic type factory with standard storage type
|
||||
@ -304,25 +303,29 @@ make_dynamic_histogram(T&& axis0, Ts&&... axis) {
|
||||
return make_dynamic_histogram_with<AnyAxisType>(S(), std::forward<T>(axis0), std::forward<Ts>(axis)...);
|
||||
}
|
||||
|
||||
/// dynamic type factory from axis iterators with custom storage type
|
||||
/// dynamic type factory with custom storage type
|
||||
template <typename Storage, typename Iterator,
|
||||
typename = detail::requires_iterator<Iterator>>
|
||||
histogram<typename Iterator::value_type, detail::rm_cv_ref<Storage>>
|
||||
histogram<mp11::mp_rename<typename Iterator::value_type, dynamic_axes>, detail::rm_cv_ref<Storage>>
|
||||
make_dynamic_histogram_with(Storage&& s, Iterator begin, Iterator end) {
|
||||
BOOST_ASSERT_MSG(std::distance(begin, end) > 0, "at least one axis required");
|
||||
using histogram_type = histogram<typename Iterator::value_type, detail::rm_cv_ref<Storage>>;
|
||||
auto axes = typename histogram_type::axes_type(begin, end, begin->get_allocator());
|
||||
return histogram_type(std::move(axes), std::move(s));
|
||||
using H = histogram<mp11::mp_rename<typename Iterator::value_type, dynamic_axes>, detail::rm_cv_ref<Storage>>;
|
||||
using alloc_type = typename mp11::mp_front<typename Iterator::value_type>::allocator_type;
|
||||
// auto axes = typename H::axes_type(static_cast<const axis::labeled_base<alloc_type>&>(*begin).get_allocator());
|
||||
// axes.reserve(std::distance(begin, end));
|
||||
// while (begin != end)
|
||||
// axes.emplace_back(*begin++);
|
||||
// return H(std::move(axes), std::forward<Storage>(s));
|
||||
return H();
|
||||
}
|
||||
|
||||
/// dynamic type factory from axis iterators with standard storage type
|
||||
template <typename Iterator, typename = detail::requires_iterator<Iterator>>
|
||||
histogram<typename Iterator::value_type>
|
||||
/// dynamic type factory with standard storage type
|
||||
template <typename Iterator,
|
||||
typename = detail::requires_iterator<Iterator>>
|
||||
histogram<mp11::mp_rename<typename Iterator::value_type, dynamic_axes>>
|
||||
make_dynamic_histogram(Iterator begin, Iterator end) {
|
||||
using S = typename histogram<typename Iterator::value_type>::storage_type;
|
||||
using S = typename histogram<mp11::mp_rename<typename Iterator::value_type, dynamic_axes>>::storage_type;
|
||||
return make_dynamic_histogram_with(S(), begin, end);
|
||||
}
|
||||
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
|
@ -125,9 +125,23 @@ void circular<T, A>::serialize(Archive& ar, unsigned /* version */) {
|
||||
template <typename T, typename A>
|
||||
template <class Archive>
|
||||
void variable<T, A>::serialize(Archive& ar, unsigned /* version */) {
|
||||
const auto old_size = base_type::size();
|
||||
value_allocator_type old_alloc = base_type::get_allocator();
|
||||
ar& boost::serialization::base_object<labeled_base<A>>(*this);
|
||||
if (Archive::is_loading::value) { x_.reset(new T[base::size() + 1]); }
|
||||
ar& boost::serialization::make_array(x_.get(), base::size() + 1);
|
||||
if (Archive::is_loading::value && old_size != base_type::size()) {
|
||||
auto xit = x_;
|
||||
auto xend = x_ + old_size + 1;
|
||||
while (xit != xend)
|
||||
old_alloc.destroy(xit++);
|
||||
old_alloc.deallocate(x_, old_size + 1);
|
||||
value_allocator_type new_alloc(base_type::get_allocator());
|
||||
x_ = new_alloc.allocate(base_type::size() + 1);
|
||||
xit = x_;
|
||||
xend = x_ + base_type::size() + 1;
|
||||
while (xit != xend)
|
||||
new_alloc.construct(xit++);
|
||||
}
|
||||
ar& boost::serialization::make_array(x_, base_type::size() + 1);
|
||||
}
|
||||
|
||||
template <typename T, typename A>
|
||||
|
@ -443,23 +443,45 @@ int main() {
|
||||
|
||||
// dynamic_axes with allocator
|
||||
{
|
||||
using inner_type = axis::integer<int, tracing_allocator<char>>;
|
||||
using axis_type = axis::any<inner_type>;
|
||||
using axes_type = dynamic_axes<inner_type>;
|
||||
using T1 = axis::regular<axis::transform::identity, double, tracing_allocator<char>>;
|
||||
using T2 = axis::circular<double, tracing_allocator<char>>;
|
||||
using T3 = axis::variable<double, tracing_allocator<char>>;
|
||||
using T4 = axis::integer<int, tracing_allocator<char>>;
|
||||
using T5 = axis::category<int, tracing_allocator<char>>;
|
||||
using axis_type = axis::any<T1, T2, T3, T4, T5>;
|
||||
using axes_type = boost::mp11::mp_rename<axis_type, dynamic_axes>;
|
||||
using expected = tracing_allocator<axis_type>;
|
||||
BOOST_TEST_TRAIT_TRUE((std::is_same<axes_type::allocator_type, expected>));
|
||||
|
||||
std::size_t allocated_bytes = 0;
|
||||
std::size_t deallocated_bytes = 0;
|
||||
tracing_allocator_db db;
|
||||
{
|
||||
auto a = tracing_allocator<char>(allocated_bytes, deallocated_bytes);
|
||||
auto axis = inner_type(0, 1, std::string(1024, 'c'), axis::uoflow_type::on, a);
|
||||
auto a = tracing_allocator<char>(db);
|
||||
const auto label = std::string(512, 'c');
|
||||
axes_type axes(a);
|
||||
axes.reserve(1);
|
||||
axes.emplace_back(std::move(axis));
|
||||
axes.reserve(5);
|
||||
axes.emplace_back(T1(1, 0, 1, label, axis::uoflow_type::on, {}, a));
|
||||
axes.emplace_back(T2(2, 0, T2::two_pi(), label, a));
|
||||
axes.emplace_back(T3({0., 1., 2.}, label, axis::uoflow_type::on, a));
|
||||
axes.emplace_back(T4(0, 4, label, axis::uoflow_type::on, a));
|
||||
axes.emplace_back(T5({1, 2, 3, 4, 5}, label, axis::uoflow_type::on, a));
|
||||
}
|
||||
BOOST_TEST_EQ(allocated_bytes, deallocated_bytes);
|
||||
BOOST_TEST_EQ(allocated_bytes, 1024 + 3 * sizeof(std::size_t) + sizeof(axis_type));
|
||||
// 5 axis::any objects
|
||||
BOOST_TEST_EQ(db[typeid(axis_type)].first, db[typeid(axis_type)].second);
|
||||
BOOST_TEST_EQ(db[typeid(axis_type)].first, 5);
|
||||
|
||||
// 5 labels
|
||||
BOOST_TEST_EQ(db[typeid(char)].first, db[typeid(char)].second);
|
||||
BOOST_TEST_GE(db[typeid(char)].first, 5 * 512);
|
||||
|
||||
// nothing to allocate for T1
|
||||
// nothing to allocate for T2
|
||||
// T3 allocates storage for bin edges
|
||||
BOOST_TEST_EQ(db[typeid(double)].first, db[typeid(double)].second);
|
||||
BOOST_TEST_EQ(db[typeid(double)].first, 3);
|
||||
// nothing to allocate for T4
|
||||
// T5 allocates storage for bimap
|
||||
BOOST_TEST_EQ(db[typeid(boost::bimap<int, int>)].first, db[typeid(boost::bimap<int, int>)].second);
|
||||
BOOST_TEST_EQ(db[typeid(boost::bimap<int, int>)].first, 1);
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
|
@ -828,22 +828,28 @@ void run_tests() {
|
||||
|
||||
// allocator support
|
||||
{
|
||||
std::size_t allocated_bytes = 0, deallocated_bytes = 0;
|
||||
tracing_allocator_db db;
|
||||
{
|
||||
tracing_allocator<char> a(allocated_bytes, deallocated_bytes);
|
||||
tracing_allocator<char> a(db);
|
||||
auto h = make_s(Tag(), array_storage<int, tracing_allocator<int>>(a),
|
||||
axis::integer<int, tracing_allocator<char>>(
|
||||
0, 1024, std::string(1024, 'c'), axis::uoflow_type::on, a));
|
||||
0, 1024, std::string(512, 'c'), axis::uoflow_type::on, a));
|
||||
h(0);
|
||||
}
|
||||
BOOST_TEST_EQ(allocated_bytes, deallocated_bytes);
|
||||
BOOST_TEST_EQ(allocated_bytes,
|
||||
// axis label
|
||||
1024 + 3 * sizeof(std::size_t) +
|
||||
// any<...>, only dynamic
|
||||
sizeof(axis::integer<int, tracing_allocator<char>>) +
|
||||
// array_storage
|
||||
1026 * sizeof(int) + 3 * sizeof(std::size_t));
|
||||
|
||||
// int allocation for array_storage
|
||||
BOOST_TEST_EQ(db[typeid(int)].first, db[typeid(int)].second);
|
||||
BOOST_TEST_GE(db[typeid(int)].first, 1024);
|
||||
|
||||
// char allocation for axis label
|
||||
BOOST_TEST_EQ(db[typeid(char)].first, db[typeid(char)].second);
|
||||
BOOST_TEST_GE(db[typeid(char)].first, 512);
|
||||
|
||||
if (Tag()) { // axis::any allocation, only for dynamic histogram
|
||||
using T = axis::any<axis::integer<int, tracing_allocator<char>>>;
|
||||
BOOST_TEST_EQ(db[typeid(T)].first, db[typeid(T)].second);
|
||||
BOOST_TEST_EQ(db[typeid(T)].first, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
#ifndef BOOST_HISTOGRAM_TEST_UTILITY_HPP_
|
||||
#define BOOST_HISTOGRAM_TEST_UTILITY_HPP_
|
||||
|
||||
#include <boost/core/typeinfo.hpp>
|
||||
#include <boost/histogram/histogram.hpp>
|
||||
#include <boost/mp11/integral.hpp>
|
||||
#include <boost/mp11/tuple.hpp>
|
||||
@ -16,13 +15,18 @@
|
||||
#include <ostream>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <typeinfo>
|
||||
#include <typeindex>
|
||||
|
||||
using i0 = boost::mp11::mp_size_t<0>;
|
||||
using i1 = boost::mp11::mp_size_t<1>;
|
||||
using i2 = boost::mp11::mp_size_t<2>;
|
||||
using i3 = boost::mp11::mp_size_t<3>;
|
||||
|
||||
namespace std { // never add to std, we only do it to get ADL working
|
||||
namespace std {
|
||||
// never add to std, we only do it to get ADL working :(
|
||||
template <typename T>
|
||||
ostream& operator<<(ostream& os, const vector<T>& v) {
|
||||
os << "[ ";
|
||||
@ -31,18 +35,20 @@ ostream& operator<<(ostream& os, const vector<T>& v) {
|
||||
return os;
|
||||
}
|
||||
|
||||
struct ostreamer {
|
||||
ostream& os;
|
||||
template <typename T>
|
||||
void operator()(const T& t) const {
|
||||
os << t << " ";
|
||||
}
|
||||
};
|
||||
namespace detail {
|
||||
struct ostreamer {
|
||||
ostream& os;
|
||||
template <typename T>
|
||||
void operator()(const T& t) const {
|
||||
os << t << " ";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
ostream& operator<<(ostream& os, const tuple<Ts...>& t) {
|
||||
os << "[ ";
|
||||
::boost::mp11::tuple_for_each(t, ostreamer{os});
|
||||
::boost::mp11::tuple_for_each(t, detail::ostreamer{os});
|
||||
os << "]";
|
||||
return os;
|
||||
}
|
||||
@ -55,8 +61,8 @@ typename Histogram::element_type sum(const Histogram& h) {
|
||||
return std::accumulate(h.begin(), h.end(), typename Histogram::element_type(0));
|
||||
}
|
||||
|
||||
struct static_tag {};
|
||||
struct dynamic_tag {};
|
||||
using static_tag = std::false_type;
|
||||
using dynamic_tag = std::true_type;
|
||||
|
||||
template <typename... Axes>
|
||||
auto make(static_tag, Axes&&... axes)
|
||||
@ -82,34 +88,35 @@ auto make_s(dynamic_tag, S&& s, Axes&&... axes)
|
||||
return make_dynamic_histogram_with<axis::any<detail::rm_cv_ref<Axes>...>>(s, std::forward<Axes>(axes)...);
|
||||
}
|
||||
|
||||
using tracing_allocator_db = std::unordered_map<
|
||||
std::type_index,
|
||||
std::pair<std::size_t, std::size_t>
|
||||
>;
|
||||
|
||||
template <class T>
|
||||
struct tracing_allocator {
|
||||
using value_type = T;
|
||||
|
||||
std::size_t* allocated_bytes = nullptr;
|
||||
std::size_t* deallocated_bytes = nullptr;
|
||||
tracing_allocator_db* db = nullptr;
|
||||
|
||||
tracing_allocator() noexcept {}
|
||||
tracing_allocator(std::size_t& b, std::size_t& d) noexcept : allocated_bytes(&b),
|
||||
deallocated_bytes(&d) {}
|
||||
tracing_allocator(tracing_allocator_db& x) noexcept : db(&x) {}
|
||||
template <class U>
|
||||
tracing_allocator(const tracing_allocator<U>& a) noexcept
|
||||
: allocated_bytes(a.allocated_bytes),
|
||||
deallocated_bytes(a.deallocated_bytes) {}
|
||||
: db(a.db) {}
|
||||
~tracing_allocator() noexcept {}
|
||||
|
||||
T* allocate(std::size_t n) {
|
||||
const auto& ti = BOOST_CORE_TYPEID(T);
|
||||
std::cerr << "alloc " << n << " x " << boost::core::demangled_name(ti) << " = "
|
||||
<< (n * sizeof(T)) << std::endl;
|
||||
if (allocated_bytes) *allocated_bytes += n * sizeof(T);
|
||||
if (db) {
|
||||
(*db)[typeid(T)].first += n;
|
||||
}
|
||||
return static_cast<T*>(::operator new(n * sizeof(T)));
|
||||
}
|
||||
|
||||
void deallocate(T*& p, std::size_t n) {
|
||||
const auto& ti = BOOST_CORE_TYPEID(T);
|
||||
std::cerr << "dealloc " << n << " x " << boost::core::demangled_name(ti) << " = "
|
||||
<< (n * sizeof(T)) << std::endl;
|
||||
if (deallocated_bytes) *deallocated_bytes += n * sizeof(T);
|
||||
if (db) {
|
||||
(*db)[typeid(T)].second += n;
|
||||
}
|
||||
::operator delete((void*)p);
|
||||
}
|
||||
};
|
||||
|
@ -32,18 +32,17 @@ int main() {
|
||||
|
||||
// tracing_allocator
|
||||
{
|
||||
std::set<std::string> types;
|
||||
std::size_t allocated_bytes = 0;
|
||||
std::size_t deallocated_bytes = 0;
|
||||
tracing_allocator<char> a(types, allocated_bytes, deallocated_bytes);
|
||||
tracing_allocator_db db;
|
||||
tracing_allocator<char> a(db);
|
||||
auto p1 = a.allocate(2);
|
||||
a.deallocate(p1, 2);
|
||||
tracing_allocator<int> b(a);
|
||||
auto p2 = b.allocate(3);
|
||||
b.deallocate(p2, 3);
|
||||
auto expected = {"char", "int"};
|
||||
BOOST_TEST_ALL_EQ(types.begin(), types.end(), expected.begin(), expected.end());
|
||||
BOOST_TEST_EQ(allocated_bytes, 2 + 3 * sizeof(int));
|
||||
BOOST_TEST_EQ(allocated_bytes, deallocated_bytes);
|
||||
BOOST_TEST_EQ(db.size(), 2);
|
||||
BOOST_TEST_EQ(db[typeid(char)].first, 2);
|
||||
BOOST_TEST_EQ(db[typeid(char)].second, 2);
|
||||
BOOST_TEST_EQ(db[typeid(int)].first, 3);
|
||||
BOOST_TEST_EQ(db[typeid(int)].second, 3);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user