Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RBinWithError.hxx
Go to the documentation of this file.
1/// \file
2/// \warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes.
3/// Feedback is welcome!
4
5#ifndef ROOT_RBinWithError
6#define ROOT_RBinWithError
7
8#include "RHistUtils.hxx"
9
10#include <cmath>
11#include <cstring>
12
13namespace ROOT {
14namespace Experimental {
15
16/**
17A special bin content type to compute the bin error in weighted filling.
18
19\warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes.
20Feedback is welcome!
21*/
23 double fSum = 0;
24 double fSum2 = 0;
25
26 explicit operator float() const { return fSum; }
27 explicit operator double() const { return fSum; }
28
30 {
31 fSum++;
32 fSum2++;
33 return *this;
34 }
35
37 {
38 RBinWithError old = *this;
39 operator++();
40 return old;
41 }
42
44 {
45 fSum += w;
46 fSum2 += w * w;
47 return *this;
48 }
49
51 {
52 fSum += rhs.fSum;
53 fSum2 += rhs.fSum2;
54 return *this;
55 }
56
57 RBinWithError &operator*=(double factor)
58 {
59 fSum *= factor;
60 fSum2 *= factor * factor;
61 return *this;
62 }
63
64private:
65 void AtomicAdd(double a, double a2)
66 {
67 // The sum of squares is always non-negative. Use the sign bit to implement a cheap spin lock.
68 double origSum2;
70
71 while (true) {
72 // Repeat loads from memory until we see a non-negative value.
73 // NB: do not use origSum2 < 0, it does not work for -0.0!
74 while (std::signbit(origSum2)) {
76 }
77
78 // The variable appears to be unlocked, confirm with a compare-exchange. It uses acquire memory order in case
79 // of success, so the release store synchronizes with this load. This ensures that the previous write of fSum
80 // becomes a visible side-effect in this thread and the access below will see it.
81 double negated = std::copysign(origSum2, -1.0);
83 break;
84 }
85 }
86
87 // By using a spin lock, we would not need atomic operations for fSum. However, we must use a release store to
88 // guarantee correctness of AtomicLoad.
89 double sum;
91 sum += a;
93
94 double sum2 = origSum2 + a2;
95 // This store synchronizes with the compare-exchange, see above.
97 }
98
99public:
100 void AtomicIncRelease() { AtomicAdd(1.0, 1.0); }
101
102 void AtomicAddRelease(double w) { AtomicAdd(w, w * w); }
103
104 /// Add another bin content using atomic instructions.
105 ///
106 /// \param[in] rhs another bin content that must not be modified during the operation
107 void AtomicAdd(const RBinWithError &rhs) { AtomicAdd(rhs.fSum, rhs.fSum2); }
108
110 {
111 double origSum2;
113
114 while (true) {
115 // Repeat loads from memory until we see a non-negative value.
116 // NB: do not use origSum2 < 0, it does not work for -0.0!
117 while (std::signbit(origSum2)) {
119 }
120
121 // The release store to fSum2 at the end of AtomicAdd synchronizes with the last acquire load above. This
122 // ensures that the previous write of fSum becomes a visible side-effect in this thread and the atomic load
123 // below will see it.
124
126
127 // In the reverse direction, the release store of fSum synchronizes with the acquire load above. This ensures
128 // that the previous write of fSum2 becomes a visible side-effect and we would notice if the spinlock is taken.
129
130 // NB: an acquire load is required here in case the comparison fails below and we repeat the outer loop.
132
133 // Check if the value of fSum2 is still identical to the first load.
134 // NB: do not use origSum2 == ret->fSum2, it is problematic because 0.0 == -0.0!
135 if (!std::memcmp(&origSum2, &ret->fSum2, sizeof(origSum2))) {
136 return;
137 }
138
139 // The comparison failed, an update happened or is in progress.
140 origSum2 = ret->fSum2;
141 }
142 }
143};
144
145} // namespace Experimental
146} // namespace ROOT
147
148#endif
#define a(i)
Definition RSha256.hxx:99
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
std::enable_if_t< std::is_arithmetic_v< T >, bool > AtomicCompareExchangeAcquire(T *ptr, T *expected, T *desired)
std::enable_if_t< std::is_arithmetic_v< T > > AtomicLoadAcquire(const T *ptr, T *ret)
std::enable_if_t< std::is_arithmetic_v< T > > AtomicStoreRelease(T *ptr, T *val)
std::enable_if_t< std::is_arithmetic_v< T > > AtomicLoad(const T *ptr, T *ret)
A special bin content type to compute the bin error in weighted filling.
RBinWithError & operator+=(double w)
RBinWithError & operator+=(const RBinWithError &rhs)
void AtomicLoad(RBinWithError *ret) const
RBinWithError & operator*=(double factor)
void AtomicAdd(double a, double a2)
void AtomicAdd(const RBinWithError &rhs)
Add another bin content using atomic instructions.
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2338