11#include " hist_test.hxx"
22
3+ #include < atomic>
4+ #include < cstddef>
5+
36TEST (RHistEngine, AddAtomic)
47{
58 static constexpr std::size_t Bins = 20 ;
@@ -24,6 +27,27 @@ TEST(RHistEngine, AddAtomic)
2427 EXPECT_EQ (engineA.GetBinContent (RBinIndex::Overflow ()), 1 );
2528}
2629
30+ TEST (RHistEngine, StressAddAtomic)
31+ {
32+ static constexpr std::size_t NThreads = 4 ;
33+ static constexpr std::size_t NAddsPerThread = 10000 ;
34+ static constexpr std::size_t NAdds = NThreads * NAddsPerThread;
35+
36+ // Fill a single bin, to maximize contention.
37+ const RRegularAxis axis (1 , {0 , 1 });
38+ RHistEngine<int > engineA ({axis});
39+ RHistEngine<int > engineB ({axis});
40+ engineB.Fill (0.5 );
41+
42+ StressInParallel (NThreads, [&] {
43+ for (std::size_t i = 0 ; i < NAddsPerThread; i++) {
44+ engineA.AddAtomic (engineB);
45+ }
46+ });
47+
48+ EXPECT_EQ (engineA.GetBinContent (0 ), NAdds);
49+ }
50+
2751TEST (RHistEngine, AddAtomicDifferent)
2852{
2953 // The equality operators of RAxes and the axis objects are already unit-tested separately, so here we only check one
@@ -213,6 +237,36 @@ TEST(RHistEngine, FillAtomicTupleWeightInvalidNumberOfArguments)
213237 EXPECT_THROW (engine2.FillAtomic (std::make_tuple (1 , 2 , 3 ), RWeight (1 )), std::invalid_argument);
214238}
215239
240+ TEST (RHistEngine, StressFillAddAtomicWeight)
241+ {
242+ static constexpr std::size_t NThreads = 4 ;
243+ static constexpr std::size_t NOpsPerThread = 10000 ;
244+ static constexpr std::size_t NOps = NThreads * NOpsPerThread;
245+ static constexpr double Weight = 0.5 ;
246+
247+ // Fill a single bin, to maximize contention.
248+ const RRegularAxis axis (1 , {0 , 1 });
249+ RHistEngine<float > engineA ({axis});
250+ RHistEngine<float > engineB ({axis});
251+ engineB.Fill (0.5 , RWeight (Weight));
252+
253+ std::atomic<int > op = 0 ;
254+ StressInParallel (NThreads, [&] {
255+ int chosenOp = op.fetch_add (1 ) % 2 ;
256+ if (chosenOp == 0 ) {
257+ for (std::size_t i = 0 ; i < NOpsPerThread; i++) {
258+ engineA.FillAtomic (0.5 , RWeight (Weight));
259+ }
260+ } else {
261+ for (std::size_t i = 0 ; i < NOpsPerThread; i++) {
262+ engineA.AddAtomic (engineB);
263+ }
264+ }
265+ });
266+
267+ EXPECT_EQ (engineA.GetBinContent (0 ), NOps * Weight);
268+ }
269+
216270TEST (RHistEngine_RBinWithError, AddAtomic)
217271{
218272 static constexpr std::size_t Bins = 20 ;
@@ -236,6 +290,28 @@ TEST(RHistEngine_RBinWithError, AddAtomic)
236290 }
237291}
238292
293+ TEST (RHistEngine_RBinWithError, StressAddAtomic)
294+ {
295+ static constexpr std::size_t NThreads = 4 ;
296+ static constexpr std::size_t NAddsPerThread = 10000 ;
297+ static constexpr std::size_t NAdds = NThreads * NAddsPerThread;
298+
299+ // Fill a single bin, to maximize contention.
300+ const RRegularAxis axis (1 , {0 , 1 });
301+ RHistEngine<RBinWithError> engineA ({axis});
302+ RHistEngine<RBinWithError> engineB ({axis});
303+ engineB.Fill (0.5 );
304+
305+ StressInParallel (NThreads, [&] {
306+ for (std::size_t i = 0 ; i < NAddsPerThread; i++) {
307+ engineA.AddAtomic (engineB);
308+ }
309+ });
310+
311+ EXPECT_EQ (engineA.GetBinContent (0 ).fSum , NAdds);
312+ EXPECT_EQ (engineA.GetBinContent (0 ).fSum2 , NAdds);
313+ }
314+
239315TEST (RHistEngine_RBinWithError, FillAtomic)
240316{
241317 static constexpr std::size_t Bins = 20 ;
0 commit comments