Skip to content
Merged
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
11 changes: 11 additions & 0 deletions hist/histv7/doc/CodeArchitecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ It can be used as a template argument to `RHistEngine` and `RHist`.
A wrapper `struct` for a single `double` value, used for weighted filling to distinguish its type.
Objects of this type are passed by value.

## Classes for Concurrent Filling

### `RHistConcurrentFiller`

A class to orchestrate concurrent filling of `RHist` by creating (multiple) fill contexts.

### `RHistFillContext`

Parallel user code uses contexts to fill `RHist`s concurrently.
Each instance has a local `RHistStats` object to avoid contention on the global histogram statistics.

## Auxiliary Classes

### `RBinIndex`
Expand Down
2 changes: 1 addition & 1 deletion hist/histv7/doc/ConcurrentFilling.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ For large histograms and reasonable data, contention on individual bins is expec
On the other hand, updates of the (global) histogram statistics (`RHistStats`) can easily lead to contention.
For this reason, `RHist` does **not** offer a `FillAtomic` method because it cannot be implemented efficiently.
Instead, the user has to create a `RHistConcurrentFiller` and (potentially many) `RHistFillContext`s.
These will work together to accumulate the (global) histogram statistics during concurrent filling.
These work together to accumulate the (global) histogram statistics during concurrent filling.
2 changes: 2 additions & 0 deletions hist/histv7/headers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ set(histv7_headers
ROOT/RCategoricalAxis.hxx
ROOT/RHist.hxx
ROOT/RHistAutoAxisFiller.hxx
ROOT/RHistConcurrentFiller.hxx
ROOT/RHistEngine.hxx
ROOT/RHistFillContext.hxx
ROOT/RHistStats.hxx
ROOT/RHistUtils.hxx
ROOT/RLinearizedIndex.hxx
Expand Down
6 changes: 6 additions & 0 deletions hist/histv7/inc/ROOT/RHist.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ class TBuffer;
namespace ROOT {
namespace Experimental {

// forward declaration for friend declaration
template <typename BinContentType>
class RHistFillContext;

/**
A histogram for aggregation of data along multiple dimensions.

Expand Down Expand Up @@ -55,6 +59,8 @@ Feedback is welcome!
*/
template <typename BinContentType>
class RHist final {
friend class RHistFillContext<BinContentType>;

/// The histogram engine including the bin contents.
RHistEngine<BinContentType> fEngine;
/// The global histogram statistics.
Expand Down
96 changes: 96 additions & 0 deletions hist/histv7/inc/ROOT/RHistConcurrentFiller.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/// \file
/// \warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes.
/// Feedback is welcome!

#ifndef ROOT_RHistConcurrentFiller
#define ROOT_RHistConcurrentFiller

#include "RHist.hxx"
#include "RHistEngine.hxx"
#include "RHistFillContext.hxx"
#include "RWeight.hxx"

#include <exception>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <vector>

namespace ROOT {
namespace Experimental {

/**
A histogram filler to concurrently fill an RHist.

\code
auto hist = std::make_shared<ROOT::Experimental::RHist<int>>(10, std::make_pair(5, 15));
{
ROOT::Experimental::RHistConcurrentFiller filler(hist);
auto context = filler.CreateFillContext();
context.Fill(8.5);
}
// hist->GetBinContent(ROOT::Experimental::RBinIndex(3)) will return 1
\endcode

\warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes.
Feedback is welcome!
*/
template <typename BinContentType>
class RHistConcurrentFiller final {
/// A pointer to the filled histogram
std::shared_ptr<RHist<BinContentType>> fHist;

/// Mutex to protect access to the list of fill contexts (not for filling itself!)
std::mutex fMutex;
/// The list of fill contexts, for checks during destruction
std::vector<std::weak_ptr<RHistFillContext<BinContentType>>> fFillContexts;

public:
/// Create a filler object.
///
/// \param[in] hist a pointer to the histogram
explicit RHistConcurrentFiller(std::shared_ptr<RHist<BinContentType>> hist) : fHist(hist)
{
if (!hist) {
throw std::invalid_argument("hist must not be nullptr");
}
}

RHistConcurrentFiller(const RHistConcurrentFiller<BinContentType> &) = delete;
RHistConcurrentFiller(RHistConcurrentFiller<BinContentType> &&) = delete;
RHistConcurrentFiller<BinContentType> &operator=(const RHistConcurrentFiller<BinContentType> &) = delete;
RHistConcurrentFiller<BinContentType> &operator=(RHistConcurrentFiller<BinContentType> &&) = delete;

~RHistConcurrentFiller()
{
for (const auto &context : fFillContexts) {
if (!context.expired()) {
// According to C++ Core Guideline C.36 "A destructor must not fail" and (C.37) "If a destructor tries to
// exit with an exception, it’s a bad design error and the program had better terminate".
std::terminate(); // GCOVR_EXCL_LINE
}
}
}

const std::shared_ptr<RHist<BinContentType>> &GetHist() const { return fHist; }

/// Create a new context for concurrent filling.
std::shared_ptr<RHistFillContext<BinContentType>> CreateFillContext()
{
// Cannot use std::make_shared because the constructor of RHistFillContext is private. Also it would mean that the
// (direct) memory of all contexts stays around until the vector of weak_ptr's is cleared.
std::shared_ptr<RHistFillContext<BinContentType>> context(new RHistFillContext<BinContentType>(*fHist));

{
std::lock_guard g(fMutex);
fFillContexts.push_back(context);
}

return context;
}
};

} // namespace Experimental
} // namespace ROOT

#endif
116 changes: 116 additions & 0 deletions hist/histv7/inc/ROOT/RHistFillContext.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/// \file
/// \warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes.
/// Feedback is welcome!

#ifndef ROOT_RHistFillContext
#define ROOT_RHistFillContext

#include "RHist.hxx"
#include "RHistEngine.hxx"
#include "RHistStats.hxx"

namespace ROOT {
namespace Experimental {

// forward declaration for friend declaration
template <typename BinContentType>
class RHistConcurrentFiller;

/**
A context to concurrently fill an RHist.

\sa RHistConcurrentFiller

\warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes.
Feedback is welcome!
*/
template <typename BinContentType>
class RHistFillContext final {
friend class RHistConcurrentFiller<BinContentType>;

private:
/// A pointer to the filled histogram
RHist<BinContentType> *fHist = nullptr;

/// Local histogram statistics
RHistStats fStats;

/// \sa RHistConcurrentFiller::CreateFillContent()
explicit RHistFillContext(RHist<BinContentType> &hist) : fHist(&hist), fStats(hist.GetNDimensions()) {}
RHistFillContext(const RHistFillContext<BinContentType> &) = delete;
RHistFillContext(RHistFillContext<BinContentType> &&) = default;
RHistFillContext<BinContentType> &operator=(const RHistFillContext<BinContentType> &) = delete;
RHistFillContext<BinContentType> &operator=(RHistFillContext<BinContentType> &&) = default;

public:
~RHistFillContext() { Flush(); }

/// Fill an entry into the histogram.
///
/// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
/// discarded.
///
/// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
/// converted for the axis type at run-time.
///
/// \param[in] args the arguments for each axis
/// \sa RHist::Fill(const std::tuple<A...> &args)
template <typename... A>
void Fill(const std::tuple<A...> &args)
{
fHist->fEngine.FillAtomic(args);
fStats.Fill(args);
}

/// Fill an entry into the histogram with a weight.
///
/// This overload is not available for integral bin content types (see \ref RHistEngine::SupportsWeightedFilling).
///
/// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
/// discarded.
///
/// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
/// converted for the axis type at run-time.
///
/// \param[in] args the arguments for each axis
/// \param[in] weight the weight for this entry
/// \sa RHist::Fill(const std::tuple<A...> &args, RWeight weight)
template <typename... A>
void Fill(const std::tuple<A...> &args, RWeight weight)
{
fHist->fEngine.FillAtomic(args, weight);
fStats.Fill(args, weight);
}

/// Fill an entry into the histogram.
///
/// For weighted filling, pass an RWeight as the last argument. This is not available for integral bin content types
/// (see \ref RHistEngine::SupportsWeightedFilling).
///
/// If one of the arguments is outside the corresponding axis and flow bins are disabled, the entry will be silently
/// discarded.
///
/// Throws an exception if the number of arguments does not match the axis configuration, or if an argument cannot be
/// converted for the axis type at run-time.
///
/// \param[in] args the arguments for each axis
/// \sa RHist::Fill(const A &...args)
template <typename... A>
void Fill(const A &...args)
{
fHist->fEngine.FillAtomic(args...);
fStats.Fill(args...);
}

/// Flush locally accumulated entries to the histogram.
void Flush()
{
fHist->fStats.AddAtomic(fStats);
fStats.Clear();
}
};

} // namespace Experimental
} // namespace ROOT

#endif
1 change: 1 addition & 0 deletions hist/histv7/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ HIST_ADD_GTEST(hist_atomic hist_atomic.cxx)
HIST_ADD_GTEST(hist_auto hist_auto.cxx)
HIST_ADD_GTEST(hist_axes hist_axes.cxx)
HIST_ADD_GTEST(hist_categorical hist_categorical.cxx)
HIST_ADD_GTEST(hist_concurrent hist_concurrent.cxx)
HIST_ADD_GTEST(hist_engine hist_engine.cxx)
HIST_ADD_GTEST(hist_engine_atomic hist_engine_atomic.cxx)
HIST_ADD_GTEST(hist_hist hist_hist.cxx)
Expand Down
Loading
Loading