Skip to content

Commit 42cf411

Browse files
committed
[RF] Serialize RooWorkspace snapshots in a more compressed way
This commit eliminates the *huge* overhead when storing parameter snapshots in a RooWorkspace. A "snapshot" actually needs to contain very little info. What's happening right now is that for storing a Snapshot, RooFit creates a `RooRealVar` clone for each variable, which is `sizeof(RooRealVar) = 1000` bytes plus manually-allocated overhead (e.g. for binning info). This memory overhead can be eliminated in the custom `RooWorkspace::Streamer`, only storing the information necessary for a snapshot. And when reading back the file, the `RooRealVar` clones will be instantiated so there is not change in behavior for the user. Closes #18032.
1 parent 555c195 commit 42cf411

File tree

3 files changed

+99
-1
lines changed

3 files changed

+99
-1
lines changed

roofit/roofitcore/inc/LinkDef.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@
247247
#pragma link C++ class RooProjectedPdf+ ;
248248
#pragma link C++ class RooWorkspace- ;
249249
#pragma link C++ class RooWorkspace::CodeRepo- ;
250+
#pragma link C++ class RooWorkspace::Snapshot+ ;
250251
#pragma link C++ class RooWorkspace::WSDir+ ;
251252
#pragma link C++ class RooWorkspaceHandle+;
252253
#pragma link C++ class std::list<TObject*>+ ;

roofit/roofitcore/inc/RooWorkspace.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,14 @@ class RooWorkspace : public TNamed {
165165

166166
RooExpensiveObjectCache& expensiveObjectCache() { return _eocache ; }
167167

168+
struct Snapshot : public TNamed {
169+
std::vector<std::size_t> indices;
170+
std::vector<double> values;
171+
std::vector<double> errors;
172+
std::vector<std::uint8_t> isConstant;
173+
ClassDefOverride(Snapshot, 1);
174+
};
175+
168176
class CodeRepo : public TObject {
169177
public:
170178
CodeRepo(RooWorkspace* wspace=nullptr) : _wspace(wspace), _compiledOK(true) {} ;

roofit/roofitcore/src/RooWorkspace.cxx

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2432,6 +2432,67 @@ void RooWorkspace::CodeRepo::Streamer(TBuffer &R__b)
24322432
}
24332433
}
24342434

2435+
namespace {
2436+
2437+
/// \brief Serializes a RooArgSet into a RooWorkspace::Snapshot.
2438+
///
2439+
/// Converts a given set of RooRealVar objects from a RooArgSet into a compact
2440+
/// `RooWorkspace::Snapshot` structure. This involves storing the component indices
2441+
/// (from the workspace), current values, and errors of the variables.
2442+
///
2443+
/// \param workspace The RooWorkspace containing the component variables.
2444+
/// \param set The RooArgSet of RooRealVar objects to serialize.
2445+
/// \param snap The output snapshot structure to populate with the serialized data.
2446+
void argSetToSnapshot(RooWorkspace const &workspace, RooArgSet const &set, RooWorkspace::Snapshot &snap)
2447+
{
2448+
snap.SetName(set.GetName());
2449+
2450+
snap.indices.reserve(set.size());
2451+
snap.values.reserve(set.size());
2452+
snap.errors.reserve(set.size());
2453+
snap.isConstant.reserve(set.size());
2454+
2455+
// Create a hash map for fast index lookup
2456+
std::unordered_map<TNamed const*, std::size_t> indexMap;
2457+
for (std::size_t i = 0; i < workspace.components().size(); ++i) {
2458+
indexMap[workspace.components()[i]->namePtr()] = i;
2459+
}
2460+
2461+
for (auto *var : static_range_cast<RooRealVar const *>(set)) {
2462+
snap.indices.push_back(indexMap[var->namePtr()]);
2463+
snap.values.push_back(var->getVal());
2464+
snap.errors.push_back(var->getError());
2465+
snap.isConstant.push_back(var->isConstant());
2466+
}
2467+
}
2468+
2469+
/// \brief Deserializes a RooWorkspace::Snapshot into a RooArgSet.
2470+
///
2471+
/// Reconstructs a RooArgSet from the given snapshot by cloning the original
2472+
/// RooAbsArg components from the workspace and restoring their values and errors.
2473+
/// The resulting RooArgSet takes ownership of the cloned objects.
2474+
///
2475+
/// \param workspace The RooWorkspace containing the original components.
2476+
/// \param snap The snapshot structure containing the serialized state.
2477+
/// \param set The output RooArgSet to populate with restored RooRealVar objects.
2478+
void snapshotToArgSet(RooWorkspace const &workspace, RooWorkspace::Snapshot const &snap, RooArgSet &set)
2479+
{
2480+
set.setName(snap.GetName());
2481+
2482+
for (std::size_t i = 0; i < snap.values.size(); ++i) {
2483+
auto *orig = workspace.components()[snap.indices[i]];
2484+
set.addOwned(std::unique_ptr<RooAbsArg>{static_cast<RooAbsArg *>(orig->Clone())});
2485+
2486+
auto *var = static_cast<RooRealVar *>(set.get().back());
2487+
2488+
var->setVal(snap.values[i]);
2489+
var->setError(snap.errors[i]);
2490+
var->setConstant(snap.isConstant[i]);
2491+
}
2492+
}
2493+
2494+
} // namespace
2495+
24352496

24362497
////////////////////////////////////////////////////////////////////////////////
24372498
/// Stream an object of class RooWorkspace. This is a standard ROOT streamer for the
@@ -2453,7 +2514,7 @@ void RooWorkspace::Streamer(TBuffer &R__b)
24532514
}
24542515
RooAbsArg::ioStreamerPass2Finalize();
24552516

2456-
// Make expensive object cache of all objects point to intermal copy.
2517+
// Make expensive object cache of all objects point to internal copy.
24572518
// Somehow this doesn't work OK automatically
24582519
for (RooAbsArg *node : _allOwnedNodes) {
24592520
node->setExpensiveObjectCache(_eocache);
@@ -2475,6 +2536,17 @@ void RooWorkspace::Streamer(TBuffer &R__b)
24752536
}
24762537
}
24772538

2539+
// Expand the Snapshot objects to full RooArgSets
2540+
for (auto *snap : dynamic_range_cast<RooWorkspace::Snapshot const*>(_snapshots)) {
2541+
// If the type was not RooWorkspace::Snapshot, it was an old workspace
2542+
// where the RooArgSets were stored directly. So nothing to do.
2543+
if (snap) {
2544+
auto *set = new RooArgSet;
2545+
snapshotToArgSet(*this, *snap, *set);
2546+
_snapshots.Replace(snap, set);
2547+
}
2548+
}
2549+
24782550
} else {
24792551

24802552
// Make lists of external clients of WS objects, and remove those links temporarily
@@ -2531,8 +2603,25 @@ void RooWorkspace::Streamer(TBuffer &R__b)
25312603
}
25322604
}
25332605

2606+
// Temporary container to hold converted snapshots during serialization
2607+
RooLinkedList snapshots;
2608+
2609+
// Loop over existing _snapshots (each is a RooArgSet), and convert them
2610+
// to the more compact RooWorkspace::Snapshot format
2611+
for (auto *set : static_range_cast<RooArgSet const*>(_snapshots)) {
2612+
auto *snap = new RooWorkspace::Snapshot;
2613+
argSetToSnapshot(*this, *set, *snap);
2614+
snapshots.Add(snap) ;
2615+
}
2616+
2617+
// Temporarily replace _snapshots with the serialized version for writing
2618+
std::swap(snapshots, _snapshots);
2619+
25342620
R__b.WriteClassBuffer(RooWorkspace::Class(), this);
25352621

2622+
// Restore original _snapshots after serialization is complete
2623+
std::swap(snapshots, _snapshots);
2624+
25362625
// Reinstate clients here
25372626

25382627
for (auto &iterx : extClients) {

0 commit comments

Comments
 (0)