Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ROOT read rules for PortableHostCollection [13.3.x] #43241

Merged
merged 4 commits into from
Nov 13, 2023
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
2 changes: 2 additions & 0 deletions DataFormats/Portable/BuildFile.xml
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
<use name="alpaka"/>
<use name="rootcling"/>
<use name="FWCore/Utilities/" source_only="1"/>
<use name="HeterogeneousCore/AlpakaInterface" source_only="1"/>
33 changes: 31 additions & 2 deletions DataFormats/Portable/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,37 @@

`PortableHostCollection<T>` is a class template that wraps a SoA type `T` and an alpaka host buffer, which owns the
memory where the SoA is allocated. The content of the SoA is persistent, while the buffer itself is transient.
Specialisations of this template can be persisted, and can be read back also in "bare ROOT" mode, without any
dictionaries.
Specialisations of this template can be persisted, but requre specil ROOT read rules to read the data into a single
memory buffer.

The original way to declare these rules, now deprecated, is to add them to the `classes_def.xml` file. For example,
to declare the read rules for `portabletest::TestHostCollection` based on the `portabletest::TestSoA` SoA, one
would add to the same `classes_def.xml` file where `portabletest::TestHostCollection` is declared:
```xml
<read
sourceClass="portabletest::TestHostCollection"
targetClass="portabletest::TestHostCollection"
version="[1-]"
source="portabletest::TestSoA layout_;"
target="buffer_,layout_,view_"
embed="false">
<![CDATA[
portabletest::TestHostCollection::ROOTReadStreamer(newObj, onfile.layout_);
]]>
```

The new, recommended way to declare these rules is to create a `classes.cc` file along with `classes.h`, and use the
`SET_PORTABLEHOSTCOLLECTION_READ_RULES`. For example, to declare the rules for `portabletest::TestHostCollection` one
would create the file `classes.cc` with the content:
```c++
#include "DataFormats/Portable/interface/PortableHostCollectionReadRules.h"
#include "DataFormats/PortableTestObjects/interface/TestHostCollection.h"

SET_PORTABLEHOSTCOLLECTION_READ_RULES(portabletest::TestHostCollection);
```

`PortableHostCollection<T>` can also be read back in "bare ROOT" mode, without any dictionaries.

They have no implicit or explicit references to alpaka (neither as part of the class signature nor as part of its name).
This could make it possible to read them back with different portability solutions in the future.

Expand Down
5 changes: 4 additions & 1 deletion DataFormats/Portable/interface/PortableHostCollection.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,13 @@ class PortableHostCollection {

// part of the ROOT read streamer
static void ROOTReadStreamer(PortableHostCollection* newObj, Layout& layout) {
// destroy the default-constructed collection
newObj->~PortableHostCollection();
// use the global "host" object returned by cms::alpakatools::host()
// construct in-place a new collection, with the known size, using the global "host" object returned by cms::alpakatools::host()
new (newObj) PortableHostCollection(layout.metadata().size(), cms::alpakatools::host());
// copy the data from the on-file layout to the new collection
newObj->layout_.ROOTReadStreamer(layout);
// free the memory allocated by ROOT
layout.ROOTStreamerCleaner();
}

Expand Down
76 changes: 76 additions & 0 deletions DataFormats/Portable/interface/PortableHostCollectionReadRules.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#ifndef DataFormats_Portable_interface_PortableHostCollectionReadRules_h
#define DataFormats_Portable_interface_PortableHostCollectionReadRules_h

#include <TGenericClassInfo.h>
#include <TVirtualObject.h>

#include "DataFormats/Portable/interface/PortableHostCollection.h"
#include "FWCore/Utilities/interface/concatenate.h"
#include "FWCore/Utilities/interface/stringize.h"

// read function for PortableHostCollection, called for every event
template <typename T>
static void readPortableHostCollection_v1(char *target, TVirtualObject *from_buffer) {
// extract the actual types
using Collection = T;
using Layout = typename Collection::Layout;

// valid only for PortableHostCollection<T>
static_assert(std::is_same_v<Collection, PortableHostCollection<Layout>>);

// proxy for the object being read from file
struct OnFile {
Layout &layout_;
};

// address in memory of the buffer containing the object being read from file
char *address = static_cast<char *>(from_buffer->GetObject());
// offset of the "layout_" data member
static ptrdiff_t layout_offset = from_buffer->GetClass()->GetDataMemberOffset("layout_");
// reference to the Layout object being read from file
OnFile onfile = {*(Layout *)(address + layout_offset)};

// pointer to the Collection object being constructed in memory
Collection *newObj = (Collection *)target;

// move the data from the on-file layout to the newly constructed object
Collection::ROOTReadStreamer(newObj, onfile.layout_);
}

// put set_PortableHostCollection_read_rules in the ROOT namespace to let it forward declare GenerateInitInstance
namespace ROOT {

// set the read rules for PortableHostCollection<T>;
// this is called only once, when the dictionary is loaded.
template <typename T>
static bool set_PortableHostCollection_read_rules(std::string const &type) {
// forward declaration
TGenericClassInfo *GenerateInitInstance(T const *);

// build the read rules
std::vector<ROOT::Internal::TSchemaHelper> readrules(1);
ROOT::Internal::TSchemaHelper &rule = readrules[0];
rule.fTarget = "buffer_,layout_,view_";
rule.fSourceClass = type;
rule.fSource = type + "::Layout layout_;";
rule.fCode = type + "::ROOTReadStreamer(newObj, onfile.layout_)";
rule.fVersion = "[1-]";
rule.fChecksum = "";
rule.fInclude = "";
rule.fEmbed = false;
rule.fFunctionPtr = reinterpret_cast<void *>(::readPortableHostCollection_v1<T>);
rule.fAttributes = "";

// set the read rules
TGenericClassInfo *instance = GenerateInitInstance((T const *)nullptr);
instance->SetReadRules(readrules);

return true;
}
} // namespace ROOT

#define SET_PORTABLEHOSTCOLLECTION_READ_RULES(COLLECTION) \
static bool EDM_CONCATENATE(set_PortableHostCollection_read_rules_done_at_, __LINE__) [[maybe_unused]] = \
ROOT::set_PortableHostCollection_read_rules<COLLECTION>(EDM_STRINGIZE(COLLECTION))

#endif // DataFormats_Portable_interface_PortableHostCollectionReadRules_h
4 changes: 4 additions & 0 deletions DataFormats/PortableTestObjects/src/classes.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#include "DataFormats/Portable/interface/PortableHostCollectionReadRules.h"
#include "DataFormats/PortableTestObjects/interface/TestHostCollection.h"

SET_PORTABLEHOSTCOLLECTION_READ_RULES(portabletest::TestHostCollection);
12 changes: 0 additions & 12 deletions DataFormats/PortableTestObjects/src/classes_def.xml
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
<lcgdict>
<class name="portabletest::TestSoA"/>
<class name="portabletest::TestSoA::View"/>

<class name="portabletest::TestHostCollection"/>
<read
sourceClass="portabletest::TestHostCollection"
targetClass="portabletest::TestHostCollection"
version="[1-]"
source="portabletest::TestSoA layout_;"
target="buffer_,layout_,view_"
embed="false">
<![CDATA[
portabletest::TestHostCollection::ROOTReadStreamer(newObj, onfile.layout_);
]]>
</read>
<class name="edm::Wrapper<portabletest::TestHostCollection>" splitLevel="0"/>
</lcgdict>