Skip to content

Commit 2a971f1

Browse files
committed
[hist] Extend FillAtomic for non-arithmetic types
This includes RBinWithError, but also user-defined bin content types.
1 parent 571f419 commit 2a971f1

File tree

4 files changed

+95
-0
lines changed

4 files changed

+95
-0
lines changed

hist/histv7/inc/ROOT/RBinWithError.hxx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#ifndef ROOT_RBinWithError
66
#define ROOT_RBinWithError
77

8+
#include "RHistUtils.hxx"
9+
810
namespace ROOT {
911
namespace Experimental {
1012

@@ -45,6 +47,18 @@ struct RBinWithError final {
4547
fSum2 += rhs.fSum2;
4648
return *this;
4749
}
50+
51+
void AtomicInc()
52+
{
53+
Internal::AtomicInc(&fSum);
54+
Internal::AtomicInc(&fSum2);
55+
}
56+
57+
void AtomicAdd(double w)
58+
{
59+
Internal::AtomicAdd(&fSum, w);
60+
Internal::AtomicAdd(&fSum2, w * w);
61+
}
4862
};
4963

5064
} // namespace Experimental

hist/histv7/inc/ROOT/RHistUtils.hxx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,18 @@ std::enable_if_t<std::is_arithmetic_v<T>> AtomicInc(T *ptr)
196196
AtomicAdd(ptr, static_cast<T>(1));
197197
}
198198

199+
template <typename T, typename U>
200+
std::enable_if_t<std::is_member_function_pointer_v<decltype(&T::AtomicAdd)>> AtomicAdd(T *ptr, const U &add)
201+
{
202+
ptr->AtomicAdd(add);
203+
}
204+
205+
template <typename T>
206+
std::enable_if_t<std::is_member_function_pointer_v<decltype(&T::AtomicInc)>> AtomicInc(T *ptr)
207+
{
208+
ptr->AtomicInc();
209+
}
210+
199211
} // namespace Internal
200212
} // namespace Experimental
201213
} // namespace ROOT

hist/histv7/test/hist_engine_atomic.cxx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,38 @@ TEST(RHistEngine, FillTupleWeightInvalidNumberOfArguments)
143143
EXPECT_NO_THROW(engine2.FillAtomic(std::make_tuple(1, 2), RWeight(1)));
144144
EXPECT_THROW(engine2.FillAtomic(std::make_tuple(1, 2, 3), RWeight(1)), std::invalid_argument);
145145
}
146+
147+
TEST(RHistEngine_RBinWithError, FillAtomic)
148+
{
149+
static constexpr std::size_t Bins = 20;
150+
const RRegularAxis axis(Bins, {0, Bins});
151+
RHistEngine<RBinWithError> engine({axis});
152+
153+
for (std::size_t i = 0; i < Bins; i++) {
154+
engine.FillAtomic(i);
155+
}
156+
157+
for (auto index : axis.GetNormalRange()) {
158+
auto &bin = engine.GetBinContent(index);
159+
EXPECT_EQ(bin.fSum, 1);
160+
EXPECT_EQ(bin.fSum2, 1);
161+
}
162+
}
163+
164+
TEST(RHistEngine_RBinWithError, FillAtomicWeight)
165+
{
166+
static constexpr std::size_t Bins = 20;
167+
const RRegularAxis axis(Bins, {0, Bins});
168+
RHistEngine<RBinWithError> engine({axis});
169+
170+
for (std::size_t i = 0; i < Bins; i++) {
171+
engine.FillAtomic(i, RWeight(0.1 + i * 0.03));
172+
}
173+
174+
for (auto index : axis.GetNormalRange()) {
175+
auto &bin = engine.GetBinContent(index);
176+
double weight = 0.1 + index.GetIndex() * 0.03;
177+
EXPECT_FLOAT_EQ(bin.fSum, weight);
178+
EXPECT_FLOAT_EQ(bin.fSum2, weight * weight);
179+
}
180+
}

hist/histv7/test/hist_user.cxx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ struct User {
3030
fValue += rhs.fValue;
3131
return *this;
3232
}
33+
34+
void AtomicInc() { ROOT::Experimental::Internal::AtomicInc(&fValue); }
35+
36+
void AtomicAdd(double w) { ROOT::Experimental::Internal::AtomicAdd(&fValue, w); }
3337
};
3438

3539
static_assert(std::is_nothrow_move_constructible_v<RHistEngine<User>>);
@@ -119,3 +123,33 @@ TEST(RHistEngineUser, FillWeight)
119123
std::array<RBinIndex, 1> indices = {9};
120124
EXPECT_EQ(engine.GetBinContent(indices).fValue, 0.9);
121125
}
126+
127+
TEST(RHistEngineUser, FillAtomic)
128+
{
129+
// Unweighted filling with atomic instructions uses AtomicInc
130+
static constexpr std::size_t Bins = 20;
131+
const RRegularAxis axis(Bins, {0, Bins});
132+
RHistEngine<User> engine({axis});
133+
134+
engine.FillAtomic(8.5);
135+
engine.FillAtomic(std::make_tuple(9.5));
136+
137+
EXPECT_EQ(engine.GetBinContent(RBinIndex(8)).fValue, 1);
138+
std::array<RBinIndex, 1> indices = {9};
139+
EXPECT_EQ(engine.GetBinContent(indices).fValue, 1);
140+
}
141+
142+
TEST(RHistEngineUser, FillAtomicWeight)
143+
{
144+
// Weighted filling with atomic instructions uses AtomicAdd
145+
static constexpr std::size_t Bins = 20;
146+
const RRegularAxis axis(Bins, {0, Bins});
147+
RHistEngine<User> engine({axis});
148+
149+
engine.FillAtomic(8.5, RWeight(0.8));
150+
engine.FillAtomic(std::make_tuple(9.5), RWeight(0.9));
151+
152+
EXPECT_EQ(engine.GetBinContent(RBinIndex(8)).fValue, 0.8);
153+
std::array<RBinIndex, 1> indices = {9};
154+
EXPECT_EQ(engine.GetBinContent(indices).fValue, 0.9);
155+
}

0 commit comments

Comments
 (0)