Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions hist/histv7/inc/ROOT/RHistEngine.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ class RHistEngine final {
/// The bin contents for this histogram
std::vector<BinContentType> fBinContents;

/// Flag to pause filling while a snapshot is ongoing
mutable std::atomic<bool> fSnapshot{false}; //!

public:
/// Construct a histogram engine.
///
Expand Down Expand Up @@ -134,7 +137,7 @@ public:
/// Efficiently move construct a histogram engine.
///
/// After this operation, the moved-from object is invalid.
RHistEngine(RHistEngine &&) = default;
RHistEngine(RHistEngine &&rhs) noexcept : fAxes(std::move(rhs.fAxes)), fBinContents(std::move(rhs.fBinContents)) {}

/// The copy assignment operator is deleted.
///
Expand All @@ -144,7 +147,12 @@ public:
/// Efficiently move a histogram engine.
///
/// After this operation, the moved-from object is invalid.
RHistEngine &operator=(RHistEngine &&) = default;
RHistEngine &operator=(RHistEngine &&rhs) noexcept
{
std::swap(fAxes, rhs.fAxes);
std::swap(fBinContents, rhs.fBinContents);
return *this;
}

~RHistEngine() = default;

Expand Down Expand Up @@ -513,6 +521,10 @@ public:
template <typename... A>
void FillAtomic(const std::tuple<A...> &args)
{
while (fSnapshot.load(std::memory_order_relaxed)) {
// Spin while a snapshot is running
}

// We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
// be confusing for users.
if (sizeof...(A) != GetNDimensions()) {
Expand All @@ -537,6 +549,10 @@ public:
{
static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");

while (fSnapshot.load(std::memory_order_relaxed)) {
// Spin while a snapshot is running
}

// We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
// be confusing for users.
if (sizeof...(A) != GetNDimensions()) {
Expand All @@ -562,6 +578,10 @@ public:
static_assert(std::is_class_v<BinContentType>,
"user-defined weight types are only supported for user-defined bin content types");

while (fSnapshot.load(std::memory_order_relaxed)) {
// Spin while a snapshot is running
}

// We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
// be confusing for users.
if (sizeof...(A) != GetNDimensions()) {
Expand All @@ -583,6 +603,10 @@ public:
{
static_assert(sizeof...(A) >= 1, "need at least one argument to Fill");
if constexpr (sizeof...(A) >= 1) {
while (fSnapshot.load(std::memory_order_relaxed)) {
// Spin while a snapshot is running
}

auto t = std::forward_as_tuple(args...);
if constexpr (std::is_same_v<typename Internal::LastType<A...>::type, RWeight>) {
static_assert(SupportsWeightedFilling, "weighted filling is not supported for integral bin content types");
Expand Down Expand Up @@ -846,6 +870,12 @@ public:
static_assert(std::is_trivially_copyable_v<BinContentType>,
"snapshotting requires a trivially copyable bin content type");

do {
while (fSnapshot.load(std::memory_order_relaxed)) {
// Spin while another snapshot is running
}
} while (fSnapshot.exchange(true, std::memory_order_relaxed));

RHistEngine snapshot(fAxes.Get());
Comment thread
hahnjo marked this conversation as resolved.
// Do a first collect.
for (std::size_t i = 0; i < fBinContents.size(); i++) {
Expand All @@ -871,6 +901,8 @@ public:
}
} while (changed);

fSnapshot.store(false, std::memory_order_relaxed);

return snapshot;
}

Expand Down
30 changes: 29 additions & 1 deletion hist/histv7/test/hist_engine_atomic.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,34 @@ TEST(RHistEngine, SnapshotAtomic)
}

TEST(RHistEngine, StressSnapshotAtomic)
{
static constexpr std::size_t Bins = 20;
static constexpr std::size_t NThreads = 4;
static constexpr std::size_t NSnapshotsPerThread = 10000;
static constexpr int ExpectedBinContent0 = 1;

// Create a histogram with some bins that takes a bit of time to snapshot.
RHistEngine<int> engine(Bins, {0, Bins});
engine.Fill(0.5);

std::atomic<int> binContent0 = ExpectedBinContent0;

StressInParallel(NThreads, [&] {
for (std::size_t i = 0; i < NSnapshotsPerThread; i++) {
auto snapshot = engine.SnapshotAtomic();
// compare_exchange wants a non-const reference...
int expected = ExpectedBinContent0;
int actual = snapshot.GetBinContent(0);
if (actual != expected) {
binContent0.compare_exchange_strong(expected, actual);
}
}
});

EXPECT_EQ(binContent0, ExpectedBinContent0);
}

TEST(RHistEngine, StressFillSnapshotAtomic)
{
static constexpr std::size_t Bins = 20;
static constexpr std::size_t NThreads = 4;
Expand Down Expand Up @@ -485,7 +513,7 @@ TEST(RHistEngine_RBinWithError, SnapshotAtomic)
EXPECT_EQ(engineB.GetBinContent(RBinIndex::Overflow()).fSum, 1);
}

TEST(RHistEngine_RBinWithError, StressSnapshotAtomic)
TEST(RHistEngine_RBinWithError, StressFillSnapshotAtomic)
{
static constexpr std::size_t Bins = 20;
static constexpr std::size_t NThreads = 4;
Expand Down
2 changes: 2 additions & 0 deletions hist/histv7/test/hist_io.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ static void ExpectThrowOnWriteObject(const T &obj)
{
ROOT::TestSupport::CheckDiagsRAII diagRAII;
diagRAII.optionalDiag(kWarning, "TKey::TKey", "no public constructor", /*matchFullMessage=*/false);
diagRAII.optionalDiag(kWarning, "TStreamerInfo::Build", "data member \"fSnapshot\" will not be saved",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In which configuration did that appears? (Since fSnapshot is marked transient, we should not be issuing this message)

/*matchFullMessage=*/false);

TMemFile f("mem.root", "RECREATE");
EXPECT_THROW(f.WriteObject(&obj, "o"), std::runtime_error);
Expand Down
Loading