mirror of
https://github.com/boostorg/histogram.git
synced 2025-05-09 14:57:57 +00:00
deprecate accumulators::thread_safe, replace with optionally thread-safe accumulators::count (#326)
This commit is contained in:
parent
30e1a71571
commit
6cde30a366
14
.github/workflows/fast.yml
vendored
14
.github/workflows/fast.yml
vendored
@ -38,17 +38,3 @@ jobs:
|
||||
run: |
|
||||
cd build
|
||||
ctest -C Debug --output-on-failure
|
||||
|
||||
gcc5:
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: cmake
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
CXX=g++-5 cmake ..
|
||||
- name: ctest
|
||||
run: |
|
||||
cd build
|
||||
ctest -C Debug --output-on-failure
|
||||
|
4
.github/workflows/slow.yml
vendored
4
.github/workflows/slow.yml
vendored
@ -80,10 +80,10 @@ jobs:
|
||||
run: |
|
||||
./bootstrap.sh
|
||||
./b2 headers
|
||||
- name: Test cxxstd=14
|
||||
- name: Test cxxstd=14 (warnings ignored)
|
||||
run: |
|
||||
cd libs/histogram
|
||||
../../b2 $B2_OPTS toolset=gcc-5 cxxstd=14 test//all examples
|
||||
../../b2 -q -j2 toolset=gcc-5 cxxstd=14 test//all examples
|
||||
|
||||
gcc10:
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -425,7 +425,7 @@ There are two ways to generate a single histogram using several threads.
|
||||
|
||||
1. Each thread has its own copy of the histogram. Each copy is independently filled. The copies are then added in the main thread. Use this as the default when you can afford having `N` copies of the histogram in memory for `N` threads, because it allows each thread to work on its thread-local memory and utilise the CPU cache without the need to synchronise memory access. The highest performance gains are obtained in this way.
|
||||
|
||||
2. There is only one histogram which is filled concurrently by several threads. This requires using a thread-safe storage that can handle concurrent writes. The library provides the [classref boost::histogram::accumulators::thread_safe] accumulator, which combined with the [classref boost::histogram::dense_storage] provides a thread-safe storage.
|
||||
2. There is only one histogram which is filled concurrently by several threads. This requires using a thread-safe storage that can handle concurrent writes. The library provides the [classref boost::histogram::accumulators::count] accumulator with a thread-safe option, which combined with the [classref boost::histogram::dense_storage] provides a thread-safe storage.
|
||||
|
||||
[note Filling a histogram with growing axes in a multi-threaded environment is safe, but has poor performance since the histogram must be locked on each fill. The locks are required because an axis could grow each time, which changes the number of cells and cell addressing for all other threads. Even without growing axes, there is only a performance gain if the histogram is either very large or when significant time is spend in preparing the value to fill. For small histograms, threads frequently access the same cell, whose state has to be synchronised between the threads. This is slow even with atomic counters and made worse by the effect of false sharing.]
|
||||
|
||||
|
@ -26,7 +26,7 @@ int main() {
|
||||
Create histogram with container of thread-safe counters for parallel filling in
|
||||
several threads. Only filling is thread-safe, other guarantees are not given.
|
||||
*/
|
||||
auto h = make_histogram_with(dense_storage<accumulators::thread_safe<unsigned>>(),
|
||||
auto h = make_histogram_with(dense_storage<accumulators::count<unsigned, true>>(),
|
||||
axis::integer<>(0, 10));
|
||||
|
||||
/*
|
||||
|
@ -4,10 +4,11 @@
|
||||
// (See accompanying file LICENSE_1_0.txt
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_ACCUMULATORS_NUMBER_HPP
|
||||
#define BOOST_HISTOGRAM_ACCUMULATORS_NUMBER_HPP
|
||||
#ifndef BOOST_HISTOGRAM_ACCUMULATORS_COUNT_HPP
|
||||
#define BOOST_HISTOGRAM_ACCUMULATORS_COUNT_HPP
|
||||
|
||||
#include <boost/core/nvp.hpp>
|
||||
#include <boost/histogram/detail/atomic_number.hpp>
|
||||
#include <boost/histogram/fwd.hpp> // for count<>
|
||||
#include <type_traits> // for std::common_type
|
||||
|
||||
@ -16,32 +17,44 @@ namespace histogram {
|
||||
namespace accumulators {
|
||||
|
||||
/**
|
||||
Uses a C++ builtin arithmetic type to accumulate a count.
|
||||
Wraps a C++ arithmetic type with optionally thread-safe increments and adds.
|
||||
|
||||
This wrapper class may be used as a base class by users who want to add custom metadata
|
||||
to each bin of a histogram. Otherwise, arithmetic types should be used directly as
|
||||
accumulators in storages for simplicity. In other words, prefer `dense_storage<double>`
|
||||
over `dense_storage<count<double>>`, both are functionally equivalent.
|
||||
This adaptor optionally uses atomic operations to make concurrent increments and
|
||||
additions thread-safe for the stored arithmetic value, which can be integral or
|
||||
floating point. For small histograms, the performance will still be poor because of
|
||||
False Sharing, see https://en.wikipedia.org/wiki/False_sharing for details.
|
||||
|
||||
When weighted data is accumulated and high precision is required, use
|
||||
`accumulators::sum` instead. If a local variance estimate for the weight distribution
|
||||
should be computed as well (generally needed for a detailed statistical analysis), use
|
||||
`accumulators::weighted_sum`.
|
||||
Warning: Assignment is not thread-safe in this implementation, so don't assign
|
||||
concurrently.
|
||||
|
||||
This wrapper class can be used as a base class by users to add arbitrary metadata to
|
||||
each bin of a histogram.
|
||||
|
||||
When weighted samples are accumulated and high precision is required, use
|
||||
`accumulators::sum` instead (at the cost of lower performance). If a local variance
|
||||
estimate for the weight distribution should be computed as well (generally needed for a
|
||||
detailed statistical analysis), use `accumulators::weighted_sum`.
|
||||
|
||||
@tparam T C++ builtin arithmetic type (integer or floating point).
|
||||
@tparam ThreadSafe Set to true to make increments and adds thread-safe.
|
||||
*/
|
||||
template <class ValueType>
|
||||
template <class ValueType, bool ThreadSafe>
|
||||
class count {
|
||||
using internal_type =
|
||||
std::conditional_t<ThreadSafe, detail::atomic_number<ValueType>, ValueType>;
|
||||
|
||||
public:
|
||||
using value_type = ValueType;
|
||||
using const_reference = const value_type&;
|
||||
|
||||
count() = default;
|
||||
count() noexcept = default;
|
||||
|
||||
/// Initialize count to value and allow implicit conversion
|
||||
count(const_reference value) noexcept : value_(value) {}
|
||||
count(const_reference value) noexcept : value_{value} {}
|
||||
|
||||
/// Allow implicit conversion from other count
|
||||
template <class T>
|
||||
count(const count<T>& c) noexcept : count(c.value()) {}
|
||||
template <class T, bool B>
|
||||
count(const count<T, B>& c) noexcept : count{c.value()} {}
|
||||
|
||||
/// Increment count by one
|
||||
count& operator++() noexcept {
|
||||
@ -72,16 +85,20 @@ public:
|
||||
bool operator!=(const count& rhs) const noexcept { return !operator==(rhs); }
|
||||
|
||||
/// Return count
|
||||
const_reference value() const noexcept { return value_; }
|
||||
value_type value() const noexcept { return value_; }
|
||||
|
||||
// conversion to value_type must be explicit
|
||||
explicit operator value_type() const noexcept { return value_; }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
ar& make_nvp("value", value_);
|
||||
auto v = value();
|
||||
ar& make_nvp("value", v);
|
||||
value_ = v;
|
||||
}
|
||||
|
||||
static constexpr bool thread_safe() noexcept { return ThreadSafe; }
|
||||
|
||||
// begin: extra operators to make count behave like a regular number
|
||||
|
||||
count& operator*=(const count& rhs) noexcept {
|
||||
@ -114,10 +131,33 @@ public:
|
||||
|
||||
bool operator>=(const count& rhs) const noexcept { return value_ >= rhs.value_; }
|
||||
|
||||
friend bool operator==(const_reference x, const count& rhs) noexcept {
|
||||
return x == rhs.value_;
|
||||
}
|
||||
|
||||
friend bool operator!=(const_reference x, const count& rhs) noexcept {
|
||||
return x != rhs.value_;
|
||||
}
|
||||
|
||||
friend bool operator<(const_reference x, const count& rhs) noexcept {
|
||||
return x < rhs.value_;
|
||||
}
|
||||
|
||||
friend bool operator>(const_reference x, const count& rhs) noexcept {
|
||||
return x > rhs.value_;
|
||||
}
|
||||
|
||||
friend bool operator<=(const_reference x, const count& rhs) noexcept {
|
||||
return x <= rhs.value_;
|
||||
}
|
||||
friend bool operator>=(const_reference x, const count& rhs) noexcept {
|
||||
return x >= rhs.value_;
|
||||
}
|
||||
|
||||
// end: extra operators
|
||||
|
||||
private:
|
||||
value_type value_{};
|
||||
internal_type value_{};
|
||||
};
|
||||
|
||||
} // namespace accumulators
|
||||
@ -126,10 +166,10 @@ private:
|
||||
|
||||
#ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
|
||||
namespace std {
|
||||
template <class T, class U>
|
||||
struct common_type<boost::histogram::accumulators::count<T>,
|
||||
boost::histogram::accumulators::count<U>> {
|
||||
using type = boost::histogram::accumulators::count<common_type_t<T, U>>;
|
||||
template <class T, class U, bool B1, bool B2>
|
||||
struct common_type<boost::histogram::accumulators::count<T, B1>,
|
||||
boost::histogram::accumulators::count<U, B2>> {
|
||||
using type = boost::histogram::accumulators::count<common_type_t<T, U>, (B1 || B2)>;
|
||||
};
|
||||
} // namespace std
|
||||
#endif
|
||||
|
41
include/boost/histogram/accumulators/is_thread_safe.hpp
Normal file
41
include/boost/histogram/accumulators/is_thread_safe.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2021 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_ACCUMULATORS_IS_THREAD_SAFE_HPP
|
||||
#define BOOST_HISTOGRAM_ACCUMULATORS_IS_THREAD_SAFE_HPP
|
||||
|
||||
#include <boost/histogram/detail/priority.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
template <class T>
|
||||
constexpr bool is_thread_safe_impl(priority<0>) {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
constexpr auto is_thread_safe_impl(priority<1>) -> decltype(T::thread_safe()) {
|
||||
return T::thread_safe();
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace accumulators {
|
||||
|
||||
template <class T>
|
||||
struct is_thread_safe
|
||||
: std::integral_constant<bool,
|
||||
detail::is_thread_safe_impl<T>(detail::priority<1>{})> {};
|
||||
|
||||
} // namespace accumulators
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -54,9 +54,9 @@ std::basic_ostream<CharT, Traits>& handle_nonzero_width(
|
||||
|
||||
namespace accumulators {
|
||||
|
||||
template <class CharT, class Traits, class U>
|
||||
template <class CharT, class Traits, class U, bool B>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const count<U>& x) {
|
||||
const count<U, B>& x) {
|
||||
return os << x.value();
|
||||
}
|
||||
|
||||
@ -92,12 +92,15 @@ std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>&
|
||||
return detail::handle_nonzero_width(os, x);
|
||||
}
|
||||
|
||||
#include <boost/histogram/detail/ignore_deprecation_warning_begin.hpp>
|
||||
template <class CharT, class Traits, class T>
|
||||
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const thread_safe<T>& x) {
|
||||
os << static_cast<T>(x);
|
||||
return os;
|
||||
}
|
||||
#include <boost/histogram/detail/ignore_deprecation_warning_end.hpp>
|
||||
|
||||
} // namespace accumulators
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
@ -9,15 +9,18 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <boost/core/nvp.hpp>
|
||||
#include <boost/histogram/detail/priority.hpp>
|
||||
#include <boost/mp11/utility.hpp>
|
||||
#include <boost/histogram/fwd.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace accumulators {
|
||||
|
||||
/** Thread-safe adaptor for integral and floating point numbers.
|
||||
// cannot use new mechanism with accumulators::thread_safe
|
||||
template <class T>
|
||||
struct is_thread_safe<thread_safe<T>> : std::true_type {};
|
||||
|
||||
/** Thread-safe adaptor for builtin integral numbers.
|
||||
|
||||
This adaptor uses atomic operations to make concurrent increments and additions safe for
|
||||
the stored value.
|
||||
@ -27,130 +30,49 @@ namespace accumulators {
|
||||
instruction. On exotic platforms the size of the adapted number may be larger and/or the
|
||||
type may have different alignment, which means it cannot be tightly packed into arrays.
|
||||
|
||||
This implementation uses a workaround to support atomic operations on floating point
|
||||
numbers in C++14. Compiling with C++20 may increase performance for
|
||||
operations on floating point numbers. The implementation automatically uses the best
|
||||
implementation that is available.
|
||||
|
||||
@tparam T type to adapt; must be arithmetic (integer or floating point).
|
||||
@tparam T type to adapt, must be an integral type.
|
||||
*/
|
||||
template <class T>
|
||||
class thread_safe {
|
||||
static_assert(std::is_arithmetic<T>(), "");
|
||||
|
||||
class [[deprecated("use count<T, true> instead; "
|
||||
"thread_safe<T> will be removed in boost-1.79")]] thread_safe
|
||||
: public std::atomic<T> {
|
||||
public:
|
||||
using value_type = T;
|
||||
using const_reference = const T&;
|
||||
|
||||
thread_safe() noexcept : value_{static_cast<value_type>(0)} {}
|
||||
using super_t = std::atomic<T>;
|
||||
|
||||
thread_safe() noexcept : super_t(static_cast<T>(0)) {}
|
||||
// non-atomic copy and assign is allowed, because storage is locked in this case
|
||||
thread_safe(const thread_safe& o) noexcept : thread_safe{o.value()} {}
|
||||
thread_safe(const thread_safe& o) noexcept : super_t(o.load()) {}
|
||||
thread_safe& operator=(const thread_safe& o) noexcept {
|
||||
value_.store(o.value());
|
||||
super_t::store(o.load());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Allow implicit conversion from value.
|
||||
thread_safe(const_reference value) noexcept : value_{value} {}
|
||||
|
||||
/// Allow assignment from value.
|
||||
thread_safe& operator=(const_reference o) noexcept {
|
||||
value_.store(o);
|
||||
thread_safe(value_type arg) : super_t(arg) {}
|
||||
thread_safe& operator=(value_type arg) {
|
||||
super_t::store(arg);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Allow implicit conversion from other thread_safe.
|
||||
template <class U>
|
||||
thread_safe(const thread_safe<U>& o) noexcept : value_{o.value()} {}
|
||||
|
||||
/// Allow assignment from other thread_safe.
|
||||
template <class U>
|
||||
thread_safe& operator=(const thread_safe<U>& o) noexcept {
|
||||
value_.store(o.value());
|
||||
thread_safe& operator+=(const thread_safe& arg) {
|
||||
operator+=(arg.load());
|
||||
return *this;
|
||||
}
|
||||
thread_safe& operator+=(value_type arg) {
|
||||
super_t::fetch_add(arg, std::memory_order_relaxed);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Increment value by one.
|
||||
thread_safe& operator++() {
|
||||
increment_impl(detail::priority<1>{});
|
||||
operator+=(static_cast<value_type>(1));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Increment value by argument.
|
||||
thread_safe& operator+=(const_reference x) {
|
||||
add_impl(detail::priority<1>{}, x);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Add another thread_safe.
|
||||
thread_safe& operator+=(const thread_safe& o) {
|
||||
add_impl(detail::priority<1>{}, o.value());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Scale by value
|
||||
thread_safe& operator*=(const_reference value) noexcept {
|
||||
value_ *= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const thread_safe& rhs) const noexcept { return value_ == rhs.value_; }
|
||||
|
||||
bool operator!=(const thread_safe& rhs) const noexcept { return !operator==(rhs); }
|
||||
|
||||
/// Return value.
|
||||
value_type value() const noexcept { return value_.load(); }
|
||||
|
||||
// conversion to value_type must be explicit
|
||||
explicit operator value_type() const noexcept { return value_.load(); }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, unsigned /* version */) {
|
||||
auto value = value_.load();
|
||||
void serialize(Archive & ar, unsigned /* version */) {
|
||||
auto value = super_t::load();
|
||||
ar& make_nvp("value", value);
|
||||
value_.store(value);
|
||||
super_t::store(value);
|
||||
}
|
||||
|
||||
private:
|
||||
template <class U = value_type>
|
||||
auto increment_impl(detail::priority<1>) -> decltype(++std::declval<std::atomic<U>>()) {
|
||||
return ++value_;
|
||||
}
|
||||
|
||||
template <class U = value_type>
|
||||
auto add_impl(detail::priority<1>, const_reference x)
|
||||
-> decltype(std::declval<std::atomic<U>>().fetch_add(x,
|
||||
std::memory_order_relaxed)) {
|
||||
return value_.fetch_add(x, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// workaround for floating point numbers in C++14, obsolete in C++20
|
||||
template <class U = value_type>
|
||||
void increment_impl(detail::priority<0>) {
|
||||
operator+=(static_cast<value_type>(1));
|
||||
}
|
||||
|
||||
// workaround for floating point numbers in C++14, obsolete in C++20
|
||||
template <class U = value_type>
|
||||
void add_impl(detail::priority<0>, const_reference x) {
|
||||
value_type expected = value_.load();
|
||||
// if another tread changed expected value, compare_exchange returns false
|
||||
// and updates expected; we then loop and try to update again;
|
||||
// see https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange
|
||||
while (!value_.compare_exchange_weak(expected, expected + x))
|
||||
;
|
||||
}
|
||||
|
||||
friend bool operator==(const_reference x, const thread_safe& rhs) noexcept {
|
||||
return rhs.operator==(x);
|
||||
}
|
||||
|
||||
friend bool operator!=(const_reference x, const thread_safe& rhs) noexcept {
|
||||
return rhs.operator!=(x);
|
||||
}
|
||||
|
||||
std::atomic<T> value_;
|
||||
};
|
||||
|
||||
} // namespace accumulators
|
||||
|
80
include/boost/histogram/detail/atomic_number.hpp
Normal file
80
include/boost/histogram/detail/atomic_number.hpp
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright 2021 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_ATOMIC_NUMBER_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_ATOMIC_NUMBER_HPP
|
||||
|
||||
#include <atomic>
|
||||
#include <boost/histogram/detail/priority.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace histogram {
|
||||
namespace detail {
|
||||
|
||||
// copyable arithmetic type with thread-safe operator++ and operator+=
|
||||
template <class T>
|
||||
struct atomic_number : std::atomic<T> {
|
||||
static_assert(std::is_arithmetic<T>(), "");
|
||||
|
||||
using base_t = std::atomic<T>;
|
||||
using std::atomic<T>::atomic;
|
||||
|
||||
atomic_number() noexcept = default;
|
||||
atomic_number(const atomic_number& o) noexcept : std::atomic<T>{o.load()} {}
|
||||
atomic_number& operator=(const atomic_number& o) noexcept {
|
||||
this->store(o.load());
|
||||
return *this;
|
||||
}
|
||||
|
||||
// not defined for float
|
||||
atomic_number& operator++() noexcept {
|
||||
increment_impl(static_cast<base_t&>(*this), priority<1>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
// operator is not defined for floating point before C++20
|
||||
atomic_number& operator+=(const T& x) noexcept {
|
||||
add_impl(static_cast<base_t&>(*this), x, priority<1>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
// for integral types
|
||||
template <class U = T>
|
||||
auto increment_impl(std::atomic<U>& a, priority<1>) noexcept -> decltype(++a) {
|
||||
return ++a;
|
||||
}
|
||||
|
||||
// fallback implementation for floating point types
|
||||
template <class U = T>
|
||||
void increment_impl(std::atomic<U>&, priority<0>) noexcept {
|
||||
this->operator+=(static_cast<U>(1));
|
||||
}
|
||||
|
||||
// always available for integral types, in C++20 also available for float
|
||||
template <class U = T>
|
||||
static auto add_impl(std::atomic<U>& a, const U& x, priority<1>) noexcept
|
||||
-> decltype(a += x) {
|
||||
return a += x;
|
||||
}
|
||||
|
||||
// pre-C++20 fallback implementation for floating point
|
||||
template <class U = T>
|
||||
static void add_impl(std::atomic<U>& a, const U& x, priority<0>) noexcept {
|
||||
U expected = a.load();
|
||||
// if another tread changed `expected` in the meantime, compare_exchange returns
|
||||
// false and updates `expected`; we then loop and try to update again;
|
||||
// see https://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange
|
||||
while (!a.compare_exchange_weak(expected, expected + x))
|
||||
;
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace histogram
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
@ -0,0 +1,8 @@
|
||||
#if (defined(__GNUC__) || defined(__clang__))
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(push) // preserve warning settings
|
||||
#pragma warning(disable : 4996) // disable depricated localtime/gmtime warning on vc8
|
||||
#endif
|
@ -0,0 +1,6 @@
|
||||
#if (defined(__GNUC__) || defined(__clang__))
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
@ -7,13 +7,15 @@
|
||||
#ifndef BOOST_HISTOGRAM_DETAIL_TERM_INFO_HPP
|
||||
#define BOOST_HISTOGRAM_DETAIL_TERM_INFO_HPP
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#if defined __has_include
|
||||
#if __has_include(<sys/ioctl.h>) && __has_include(<unistd.h>)
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#endif
|
||||
#include <boost/config/workaround.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
@ -25,33 +27,35 @@ namespace term_info {
|
||||
class env_t {
|
||||
public:
|
||||
env_t(const char* key) {
|
||||
#if BOOST_WORKAROUND(BOOST_MSVC, >= 0) // msvc complains about using std::getenv
|
||||
_dupenv_s(&data, &size, key);
|
||||
#if defined(BOOST_MSVC) // msvc complains about using std::getenv
|
||||
_dupenv_s(&data_, &size_, key);
|
||||
#else
|
||||
data = std::getenv(key);
|
||||
if (data) size = std::strlen(data);
|
||||
data_ = std::getenv(key);
|
||||
if (data_) size_ = std::strlen(data_);
|
||||
#endif
|
||||
}
|
||||
|
||||
~env_t() {
|
||||
#if BOOST_WORKAROUND(BOOST_MSVC, >= 0)
|
||||
std::free(data);
|
||||
#if defined(BOOST_MSVC)
|
||||
std::free(data_);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool contains(const char* s) {
|
||||
const std::size_t n = std::strlen(s);
|
||||
if (size < n) return false;
|
||||
return std::strstr(data, s);
|
||||
if (size_ < n) return false;
|
||||
return std::strstr(data_, s);
|
||||
}
|
||||
|
||||
operator bool() { return size > 0; }
|
||||
operator bool() { return size_ > 0; }
|
||||
|
||||
explicit operator int() { return size ? std::atoi(data) : 0; }
|
||||
explicit operator int() { return size_ ? std::atoi(data_) : 0; }
|
||||
|
||||
const char* data() const { return data_; }
|
||||
|
||||
private:
|
||||
char* data;
|
||||
std::size_t size = 0;
|
||||
char* data_;
|
||||
std::size_t size_ = 0;
|
||||
};
|
||||
|
||||
inline bool utf8() {
|
||||
@ -67,12 +71,12 @@ inline int width() {
|
||||
#if defined TIOCGWINSZ
|
||||
struct winsize ws;
|
||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
|
||||
w = std::max(static_cast<int>(ws.ws_col), 0); // not sure if ws_col can be less than 0
|
||||
w = (std::max)(static_cast<int>(ws.ws_col), 0); // not sure if ws_col can be less than 0
|
||||
#endif
|
||||
env_t env("COLUMNS");
|
||||
const int col = std::max(static_cast<int>(env), 0);
|
||||
const int col = (std::max)(static_cast<int>(env), 0);
|
||||
// if both t and w are set, COLUMNS may be used to restrict width
|
||||
return w == 0 ? col : std::min(col, w);
|
||||
return w == 0 ? col : (std::min)(col, w);
|
||||
}
|
||||
} // namespace term_info
|
||||
|
||||
|
@ -91,7 +91,7 @@ struct sample_type;
|
||||
|
||||
namespace accumulators {
|
||||
|
||||
template <class ValueType = double>
|
||||
template <class ValueType = double, bool ThreadSafe = false>
|
||||
class count;
|
||||
|
||||
template <class ValueType = double>
|
||||
@ -110,9 +110,7 @@ template <class T>
|
||||
class thread_safe;
|
||||
|
||||
template <class T>
|
||||
struct is_thread_safe : std::false_type {};
|
||||
template <class T>
|
||||
struct is_thread_safe<thread_safe<T>> : std::true_type {};
|
||||
struct is_thread_safe;
|
||||
|
||||
} // namespace accumulators
|
||||
|
||||
|
@ -323,7 +323,7 @@ public:
|
||||
indexed_range(histogram_type& hist, Iterable&& range)
|
||||
: begin_(hist.begin(), hist), end_(hist.end(), hist) {
|
||||
auto r_begin = std::begin(range);
|
||||
assert(std::distance(r_begin, std::end(range)) == hist.rank());
|
||||
assert(std::distance(r_begin, std::end(range)) == static_cast<int>(hist.rank()));
|
||||
|
||||
begin_.indices_.hist_->for_each_axis([ca = begin_.indices_.begin(), r_begin,
|
||||
stride = std::size_t{1},
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/core/nvp.hpp>
|
||||
#include <boost/histogram/accumulators/is_thread_safe.hpp>
|
||||
#include <boost/histogram/detail/array_wrapper.hpp>
|
||||
#include <boost/histogram/detail/detect.hpp>
|
||||
#include <boost/histogram/detail/iterator_adaptor.hpp>
|
||||
|
@ -97,6 +97,8 @@ if (Threads_FOUND)
|
||||
LINK_LIBRARIES Threads::Threads)
|
||||
boost_test(TYPE run SOURCES storage_adaptor_threaded_test.cpp
|
||||
LINK_LIBRARIES Threads::Threads)
|
||||
boost_test(TYPE run SOURCES accumulators_count_thread_safe_test.cpp
|
||||
LINK_LIBRARIES Threads::Threads)
|
||||
boost_test(TYPE run SOURCES accumulators_thread_safe_test.cpp
|
||||
LINK_LIBRARIES Threads::Threads)
|
||||
|
||||
|
14
test/Jamfile
14
test/Jamfile
@ -82,8 +82,6 @@ alias cxx14 :
|
||||
[ run histogram_growing_test.cpp ]
|
||||
[ run histogram_mixed_test.cpp ]
|
||||
[ run histogram_operators_test.cpp ]
|
||||
[ run histogram_ostream_test.cpp : : : <testing.launcher>"env LANG=UTF" ]
|
||||
[ run histogram_ostream_ascii_test.cpp : : : <testing.launcher>"env LANG=FOO COLUMNS=20" ]
|
||||
[ run histogram_test.cpp ]
|
||||
[ run indexed_test.cpp ]
|
||||
[ run storage_adaptor_test.cpp ]
|
||||
@ -123,11 +121,18 @@ alias failure :
|
||||
alias threading :
|
||||
[ run histogram_threaded_test.cpp ]
|
||||
[ run storage_adaptor_threaded_test.cpp ]
|
||||
[ run accumulators_count_thread_safe_test.cpp ]
|
||||
[ run accumulators_thread_safe_test.cpp ]
|
||||
:
|
||||
<threading>multi
|
||||
;
|
||||
|
||||
# tests do not work on windows, because environment variables cannot be set
|
||||
alias not_windows :
|
||||
[ run histogram_ostream_test.cpp : : : <testing.launcher>"LANG=UTF" ]
|
||||
[ run histogram_ostream_ascii_test.cpp : : : <testing.launcher>"LANG=FOO COLUMNS=20" ]
|
||||
;
|
||||
|
||||
# warnings are off for these other boost libraries, which tend to be not warning-free
|
||||
alias accumulators : [ run boost_accumulators_support_test.cpp ] : <warnings>off ;
|
||||
alias range : [ run boost_range_support_test.cpp ] : <warnings>off ;
|
||||
@ -152,10 +157,10 @@ alias libserial :
|
||||
alias minimal : cxx14 cxx17 failure threading ;
|
||||
|
||||
# all tests
|
||||
alias all : minimal odr accumulators range units serialization ;
|
||||
alias all : minimal not_windows odr accumulators range units serialization ;
|
||||
|
||||
# all except "failure", because it is distracting during development
|
||||
alias develop : cxx14 cxx17 threading odr accumulators range units serialization ;
|
||||
alias develop : cxx14 cxx17 threading not_windows odr accumulators range units serialization ;
|
||||
|
||||
explicit minimal ;
|
||||
explicit all ;
|
||||
@ -164,6 +169,7 @@ explicit cxx14 ;
|
||||
explicit cxx17 ;
|
||||
explicit failure ;
|
||||
explicit threading ;
|
||||
explicit not_windows ;
|
||||
explicit accumulators ;
|
||||
explicit range ;
|
||||
explicit units ;
|
||||
|
@ -13,8 +13,9 @@
|
||||
using namespace boost::histogram;
|
||||
using namespace std::literals;
|
||||
|
||||
int main() {
|
||||
using c_t = accumulators::count<double>;
|
||||
template <class T, bool B>
|
||||
void run_tests() {
|
||||
using c_t = accumulators::count<T, B>;
|
||||
|
||||
c_t c;
|
||||
++c;
|
||||
@ -26,8 +27,8 @@ int main() {
|
||||
c += 2;
|
||||
BOOST_TEST_EQ(str(c), "3"s);
|
||||
|
||||
BOOST_TEST_EQ(c, 3);
|
||||
BOOST_TEST_NE(c, 2);
|
||||
BOOST_TEST_EQ(c, static_cast<T>(3));
|
||||
BOOST_TEST_NE(c, static_cast<T>(2));
|
||||
|
||||
c_t one(1), two(2), one_copy(1);
|
||||
BOOST_TEST_LT(one, two);
|
||||
@ -38,6 +39,13 @@ int main() {
|
||||
BOOST_TEST_GE(one, one_copy);
|
||||
|
||||
BOOST_TEST_EQ(c_t{} += c_t{}, c_t{});
|
||||
}
|
||||
|
||||
int main() {
|
||||
run_tests<int, false>();
|
||||
run_tests<int, true>();
|
||||
run_tests<float, false>();
|
||||
run_tests<float, true>();
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
93
test/accumulators_count_thread_safe_test.cpp
Normal file
93
test/accumulators_count_thread_safe_test.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
// 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)
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/accumulators/count.hpp>
|
||||
#include <boost/histogram/accumulators/ostream.hpp>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include "throw_exception.hpp"
|
||||
#include "utility_str.hpp"
|
||||
|
||||
using namespace boost::histogram;
|
||||
using namespace std::literals;
|
||||
|
||||
constexpr int N = 10000;
|
||||
|
||||
template <class F>
|
||||
void parallel(F f) {
|
||||
auto g = [&]() {
|
||||
for (int i = 0; i < N; ++i) f();
|
||||
};
|
||||
|
||||
std::thread a(g), b(g), c(g), d(g);
|
||||
a.join();
|
||||
b.join();
|
||||
c.join();
|
||||
d.join();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void test_on() {
|
||||
using ts_t = accumulators::count<T, true>;
|
||||
|
||||
// default ctor
|
||||
{
|
||||
ts_t i;
|
||||
BOOST_TEST_EQ(i, static_cast<T>(0));
|
||||
}
|
||||
|
||||
// ctor from value
|
||||
{
|
||||
ts_t i{1001};
|
||||
BOOST_TEST_EQ(i, static_cast<T>(1001));
|
||||
BOOST_TEST_EQ(str(i), "1001"s);
|
||||
}
|
||||
|
||||
// add null
|
||||
BOOST_TEST_EQ(ts_t{} += ts_t{}, ts_t{});
|
||||
|
||||
// add non-null
|
||||
BOOST_TEST_EQ((ts_t{} += ts_t{2}), (ts_t{2}));
|
||||
|
||||
// operator++
|
||||
{
|
||||
ts_t t;
|
||||
parallel([&]() { ++t; });
|
||||
BOOST_TEST_EQ(t, static_cast<T>(4 * N));
|
||||
}
|
||||
|
||||
// operator+= with value
|
||||
{
|
||||
ts_t t;
|
||||
parallel([&]() { t += 2; });
|
||||
BOOST_TEST_EQ(t, static_cast<T>(8 * N));
|
||||
}
|
||||
|
||||
// operator+= with another thread-safe
|
||||
{
|
||||
ts_t t, u;
|
||||
u = 2;
|
||||
parallel([&]() { t += u; });
|
||||
BOOST_TEST_EQ(t, static_cast<T>(8 * N));
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_on<int>();
|
||||
test_on<float>();
|
||||
|
||||
// copy and assignment from other thread-safe
|
||||
{
|
||||
accumulators::count<char, true> r{1};
|
||||
accumulators::count<int, true> a{r}, b;
|
||||
b = r;
|
||||
BOOST_TEST_EQ(a, 1);
|
||||
BOOST_TEST_EQ(b, 1);
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
@ -8,86 +8,31 @@
|
||||
#include <boost/histogram/accumulators/ostream.hpp>
|
||||
#include <boost/histogram/accumulators/thread_safe.hpp>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include "throw_exception.hpp"
|
||||
#include "utility_str.hpp"
|
||||
|
||||
using namespace boost::histogram;
|
||||
using namespace std::literals;
|
||||
|
||||
constexpr int N = 10000;
|
||||
|
||||
template <class F>
|
||||
void parallel(F f) {
|
||||
auto g = [&]() {
|
||||
for (int i = 0; i < N; ++i) f();
|
||||
};
|
||||
|
||||
std::thread a(g), b(g), c(g), d(g);
|
||||
a.join();
|
||||
b.join();
|
||||
c.join();
|
||||
d.join();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void test_on() {
|
||||
using ts_t = accumulators::thread_safe<T>;
|
||||
|
||||
// default ctor
|
||||
{
|
||||
ts_t i;
|
||||
BOOST_TEST_EQ(i, static_cast<T>(0));
|
||||
}
|
||||
|
||||
// ctor from value
|
||||
{
|
||||
ts_t i{1001};
|
||||
BOOST_TEST_EQ(i, static_cast<T>(1001));
|
||||
BOOST_TEST_EQ(str(i), "1001"s);
|
||||
}
|
||||
|
||||
// add null
|
||||
BOOST_TEST_EQ(ts_t{} += ts_t{}, ts_t{});
|
||||
|
||||
// add non-null
|
||||
BOOST_TEST_EQ((ts_t{} += ts_t{2}), (ts_t{2}));
|
||||
|
||||
// operator++
|
||||
{
|
||||
ts_t t;
|
||||
parallel([&]() { ++t; });
|
||||
BOOST_TEST_EQ(t, static_cast<T>(4 * N));
|
||||
}
|
||||
|
||||
// operator+= with value
|
||||
{
|
||||
ts_t t;
|
||||
parallel([&]() { t += 2; });
|
||||
BOOST_TEST_EQ(t, static_cast<T>(8 * N));
|
||||
}
|
||||
|
||||
// operator+= with another thread_safe
|
||||
{
|
||||
ts_t t, u;
|
||||
u = 2;
|
||||
parallel([&]() { t += u; });
|
||||
BOOST_TEST_EQ(t, static_cast<T>(8 * N));
|
||||
}
|
||||
}
|
||||
#include <boost/histogram/detail/ignore_deprecation_warning_begin.hpp>
|
||||
|
||||
int main() {
|
||||
test_on<int>();
|
||||
test_on<float>();
|
||||
using ts_t = accumulators::thread_safe<int>;
|
||||
|
||||
// copy and assignment from other thread_safe
|
||||
{
|
||||
accumulators::thread_safe<char> r{1};
|
||||
accumulators::thread_safe<int> a{r}, b;
|
||||
b = r;
|
||||
BOOST_TEST_EQ(a, 1);
|
||||
BOOST_TEST_EQ(b, 1);
|
||||
}
|
||||
ts_t i;
|
||||
++i;
|
||||
i += 1000;
|
||||
|
||||
BOOST_TEST_EQ(i, 1001);
|
||||
BOOST_TEST_EQ(str(i), "1001"s);
|
||||
|
||||
ts_t j{5};
|
||||
i = j;
|
||||
BOOST_TEST_EQ(i, 5);
|
||||
|
||||
BOOST_TEST_EQ(ts_t{} += ts_t{}, ts_t{});
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
#include <boost/histogram/detail/ignore_deprecation_warning_end.hpp>
|
||||
|
@ -7,8 +7,9 @@
|
||||
#include <array>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/accumulators.hpp>
|
||||
#include <boost/histogram/accumulators/mean.hpp>
|
||||
#include <boost/histogram/accumulators/ostream.hpp>
|
||||
#include <boost/histogram/accumulators/weighted_mean.hpp>
|
||||
#include <boost/histogram/algorithm/sum.hpp>
|
||||
#include <boost/histogram/axis/category.hpp>
|
||||
#include <boost/histogram/axis/integer.hpp>
|
||||
|
@ -5,12 +5,7 @@
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/accumulators/ostream.hpp>
|
||||
#include <boost/histogram/accumulators/sum.hpp>
|
||||
#include <boost/histogram/accumulators/thread_safe.hpp>
|
||||
#include <boost/histogram/accumulators/weighted_sum.hpp>
|
||||
#include <boost/histogram/axis/integer.hpp>
|
||||
#include <boost/histogram/make_histogram.hpp>
|
||||
#include <boost/histogram/ostream.hpp>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
|
@ -109,9 +109,9 @@ void run_tests() {
|
||||
BOOST_TEST_CSTR_EQ(str(h, 40).c_str(), expected);
|
||||
}
|
||||
|
||||
// regular with accumulators::thread_safe
|
||||
// regular with thread-safe accumulators::count
|
||||
{
|
||||
auto h = make_s(Tag(), dense_storage<accumulators::thread_safe<double>>(),
|
||||
auto h = make_s(Tag(), dense_storage<accumulators::count<double, true>>(),
|
||||
R2(3, -0.5, 1.0));
|
||||
h.at(0) = 1;
|
||||
h.at(1) = 10;
|
||||
@ -129,6 +129,28 @@ void run_tests() {
|
||||
BOOST_TEST_CSTR_EQ(str(h, 40).c_str(), expected);
|
||||
}
|
||||
|
||||
// regular with accumulators::thread_safe
|
||||
{
|
||||
#include <boost/histogram/detail/ignore_deprecation_warning_begin.hpp>
|
||||
auto h =
|
||||
make_s(Tag(), dense_storage<accumulators::thread_safe<int>>(), R2(3, -0.5, 1.0));
|
||||
h.at(0) = 1;
|
||||
h.at(1) = 10;
|
||||
h.at(2) = 5;
|
||||
|
||||
const auto expected = "BEGIN\n"
|
||||
"histogram(regular(3, -0.5, 1, options=none))\n"
|
||||
" ┌───────────────────────┐\n"
|
||||
"[-0.5, 0) 1 │██▎ │\n"
|
||||
"[ 0, 0.5) 10 │██████████████████████ │\n"
|
||||
"[ 0.5, 1) 5 │███████████ │\n"
|
||||
" └───────────────────────┘\n"
|
||||
"END";
|
||||
|
||||
BOOST_TEST_CSTR_EQ(str(h, 40).c_str(), expected);
|
||||
#include <boost/histogram/detail/ignore_deprecation_warning_end.hpp>
|
||||
}
|
||||
|
||||
// regular with accumulators::sum
|
||||
{
|
||||
auto h = make_s(Tag(), dense_storage<accumulators::sum<double>>(), R2(3, -0.5, 1.0));
|
||||
@ -366,7 +388,7 @@ void run_tests() {
|
||||
|
||||
int main() {
|
||||
run_tests<static_tag>();
|
||||
// run_tests<dynamic_tag>();
|
||||
run_tests<dynamic_tag>();
|
||||
|
||||
{
|
||||
// cannot make empty static histogram
|
||||
|
@ -6,8 +6,9 @@
|
||||
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/accumulators.hpp>
|
||||
#include <boost/histogram/accumulators/mean.hpp>
|
||||
#include <boost/histogram/accumulators/ostream.hpp>
|
||||
#include <boost/histogram/accumulators/weighted_mean.hpp>
|
||||
#include <boost/histogram/algorithm/sum.hpp>
|
||||
#include <boost/histogram/axis.hpp>
|
||||
#include <boost/histogram/axis/ostream.hpp>
|
||||
|
@ -5,7 +5,7 @@
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/accumulators/thread_safe.hpp>
|
||||
#include <boost/histogram/accumulators/count.hpp>
|
||||
#include <boost/histogram/algorithm/sum.hpp>
|
||||
#include <boost/histogram/axis/integer.hpp>
|
||||
#include <boost/histogram/axis/ostream.hpp>
|
||||
@ -28,7 +28,7 @@ void fill_test(const A1& a1, const A2& a2, const X& x, const Y& y) {
|
||||
auto xy = {x, y};
|
||||
h1.fill(xy);
|
||||
|
||||
auto h2 = make_s(Tag{}, dense_storage<accumulators::thread_safe<int>>(), a1, a2);
|
||||
auto h2 = make_s(Tag{}, dense_storage<accumulators::count<int, true>>(), a1, a2);
|
||||
auto run = [&h2, &x, &y](int k) {
|
||||
constexpr auto shift = n_fill / 4;
|
||||
auto xit = x.cbegin() + k * shift;
|
||||
|
@ -5,11 +5,12 @@
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/accumulators/count.hpp>
|
||||
#include <boost/histogram/accumulators/thread_safe.hpp>
|
||||
#include <boost/histogram/serialization.hpp>
|
||||
#include <boost/histogram/storage_adaptor.hpp>
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "throw_exception.hpp"
|
||||
@ -40,8 +41,12 @@ int main(int argc, char** argv) {
|
||||
join(argv[1], "storage_adaptor_serialization_test_array_unsigned.xml"));
|
||||
test_serialization<std::map<std::size_t, double>>(
|
||||
join(argv[1], "storage_adaptor_serialization_test_map_double.xml"));
|
||||
test_serialization<std::vector<accumulators::count<int, true>>>(
|
||||
join(argv[1], "storage_adaptor_serialization_test_vector_thread_safe_int.xml"));
|
||||
#include <boost/histogram/detail/ignore_deprecation_warning_begin.hpp>
|
||||
test_serialization<std::vector<accumulators::thread_safe<int>>>(
|
||||
join(argv[1], "storage_adaptor_serialization_test_vector_thread_safe_int.xml"));
|
||||
#include <boost/histogram/detail/ignore_deprecation_warning_end.hpp>
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
@ -5,10 +5,10 @@
|
||||
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include <boost/histogram/accumulators/count.hpp>
|
||||
#include <boost/histogram/accumulators/ostream.hpp>
|
||||
#include <boost/histogram/accumulators/thread_safe.hpp>
|
||||
#include "throw_exception.hpp"
|
||||
#include <boost/histogram/storage_adaptor.hpp>
|
||||
#include "throw_exception.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <deque>
|
||||
@ -48,7 +48,7 @@ void tests() {
|
||||
}
|
||||
|
||||
int main() {
|
||||
using ts_int = accumulators::thread_safe<int>;
|
||||
using ts_int = accumulators::count<int, true>;
|
||||
tests<std::vector<ts_int>>();
|
||||
tests<std::array<ts_int, 100>>();
|
||||
tests<std::deque<ts_int>>();
|
||||
|
Loading…
x
Reference in New Issue
Block a user