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
5 changes: 5 additions & 0 deletions tree/ntuple/inc/ROOT/RField/RFieldSequenceContainer.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ namespace Detail {
class RFieldVisitor;
} // namespace Detail

namespace Experimental {
class RSoAField;
}

namespace Internal {
std::unique_ptr<RFieldBase> CreateEmulatedVectorField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField,
std::string_view emulatedFromType);
Expand Down Expand Up @@ -112,6 +116,7 @@ public:
/// The type-erased field for a RVec<Type>
class RRVecField : public RFieldBase {
friend class RArrayAsRVecField; // to use the RRVecDeleter and to call ResizeRVec()
friend class ROOT::Experimental::RSoAField; // to call ResizeRVec()

// Ensures that the RVec pointed to by rvec has at least nItems valid elements
// Returns the possibly new "begin pointer" of the RVec, i.e. the pointer to the data area.
Expand Down
15 changes: 11 additions & 4 deletions tree/ntuple/inc/ROOT/RField/RFieldSoA.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <cstddef>
#include <memory>
#include <mutex>
#include <string_view>
#include <typeinfo>
#include <vector>
Expand Down Expand Up @@ -66,9 +67,18 @@ class RSoAField : public RFieldBase {
/// The offset of the RVec members in the SoA type in the order of subfields of the underlying record type.
/// In particular, the order is not necessarily the same then the order of RVec members in the SoA class.
std::vector<std::size_t> fSoAMemberOffsets;
///< A deleter returned by each record member's GetDeleter()
std::vector<std::unique_ptr<RDeleter>> fRecordMemberDeleters;
std::size_t fMaxAlignment = 1;
ROOT::Internal::RColumnIndex fNWritten;

/// For reading and writing, the RVecs of the SoA class do not have a dedicated field. The in-memory RVecs of the
/// SoA object are used directly with the subfields of the underlying record type. For splitting a SoA class object
/// (SplitValue()), however, we need actual RRVecFields so that we can recursively split the in-memory SoA value.
/// The split fields are created only when SplitField() is called.
mutable std::unique_ptr<std::vector<std::unique_ptr<ROOT::RRVecField>>> fSplitFields;
Comment thread
hahnjo marked this conversation as resolved.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we group together the vector and its mutex in a single struct/allocation?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not sure how this would work. Don't we need to acquire the mutex before we can initialize the unique pointer with the vector?

mutable std::unique_ptr<std::mutex> fLockSplitFields; ///< protects the fSplitFields member.

RSoAField(std::string_view fieldName, const RSoAField &source); ///< Used by CloneImpl
RSoAField(std::string_view fieldName, TClass *clSoA);

Expand All @@ -85,8 +95,6 @@ protected:
std::size_t AppendImpl(const void *from) final;
void ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to) final;

void ReconcileOnDiskField(const RNTupleDescriptor &) final {}

void CommitClusterImpl() final { fNWritten = 0; }

public:
Expand All @@ -104,8 +112,7 @@ public:
/// of any user object. If the class is not polymorphic, return nullptr.
/// TODO(jblomer): use information in unique pointer field
const std::type_info *GetPolymorphicTypeInfo() const;
// TODO(jblomer)
// void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;

TClass *GetSoAClass() const { return fSoAClass; }
};
Expand Down
2 changes: 2 additions & 0 deletions tree/ntuple/inc/ROOT/RFieldVisitor.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public:
virtual void VisitVectorField(const ROOT::RVectorField &field) { VisitField(field); }
virtual void VisitVectorBoolField(const ROOT::RField<std::vector<bool>> &field) { VisitField(field); }
virtual void VisitRVecField(const ROOT::RRVecField &field) { VisitField(field); }
virtual void VisitSoAField(const ROOT::Experimental::RSoAField &field) { VisitField(field); }
}; // class RFieldVisitor

} // namespace Detail
Expand Down Expand Up @@ -235,6 +236,7 @@ public:
void VisitNullableField(const ROOT::RNullableField &field) final;
void VisitEnumField(const ROOT::REnumField &field) final;
void VisitAtomicField(const ROOT::RAtomicField &field) final;
void VisitSoAField(const ROOT::Experimental::RSoAField &field) final;
};

// clang-format off
Expand Down
63 changes: 58 additions & 5 deletions tree/ntuple/src/RFieldMeta.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,10 @@ ROOT::Experimental::RSoAField::RSoAField(std::string_view fieldName, const RSoAF
fTraits = source.GetTraits();
Attach(source.fSubfields[0]->Clone(source.fSubfields[0]->GetFieldName()));
fRecordMemberFields = fSubfields[0]->GetMutableSubfields();
fRecordMemberDeleters.reserve(fRecordMemberFields.size());
for (const auto f : fRecordMemberFields)
Comment thread
jblomer marked this conversation as resolved.
fRecordMemberDeleters.emplace_back(GetDeleterOf(*f));
fLockSplitFields = std::make_unique<std::mutex>();
}

ROOT::Experimental::RSoAField::RSoAField(std::string_view fieldName, std::string_view className)
Expand Down Expand Up @@ -700,13 +704,16 @@ ROOT::Experimental::RSoAField::RSoAField(std::string_view fieldName, TClass *clS
fRecordMemberFields = fSubfields[0]->GetMutableSubfields();

std::unordered_map<std::string, std::size_t> recordFieldNameToIdx;
fRecordMemberDeleters.reserve(fRecordMemberFields.size());
recordFieldNameToIdx.reserve(fRecordMemberFields.size());
for (std::size_t i = 0; i < fRecordMemberFields.size(); ++i) {
const RFieldBase *f = fRecordMemberFields[i];
assert(!f->GetFieldName().empty());
if (f->GetFieldName()[0] == ':') {
throw RException(R__FAIL("SoA fields with inheritance are currently unsupported"));
}
recordFieldNameToIdx[f->GetFieldName()] = i;
fRecordMemberDeleters.emplace_back(GetDeleterOf(*f));
Comment thread
jblomer marked this conversation as resolved.
}

const auto *bases = fSoAClass->GetListOfBases();
Expand Down Expand Up @@ -771,6 +778,7 @@ ROOT::Experimental::RSoAField::RSoAField(std::string_view fieldName, TClass *clS
fTypeAlias = renormalizedAlias;

fTraits |= kTraitSoACollection | kTraitTypeChecksum;
fLockSplitFields = std::make_unique<std::mutex>();
}

std::unique_ptr<ROOT::RFieldBase> ROOT::Experimental::RSoAField::CloneImpl(std::string_view newName) const
Expand Down Expand Up @@ -840,9 +848,28 @@ std::size_t ROOT::Experimental::RSoAField::AppendImpl(const void *from)
return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
}

void ROOT::Experimental::RSoAField::ReadGlobalImpl(ROOT::NTupleSize_t /* globalIndex */, void * /* to */)
void ROOT::Experimental::RSoAField::ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to)
{
throw RException(R__FAIL("not yet implemented"));
// Read collection info for this entry
ROOT::NTupleSize_t N;
RNTupleLocalIndex collectionStart;
fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &N);

const auto nSoAMembers = fSoAMemberOffsets.size();
for (std::size_t i = 0; i < nSoAMembers; ++i) {
RFieldBase *memberField = fRecordMemberFields[i];
const auto memberSize = memberField->GetValueSize();
void *rvecPtr = static_cast<unsigned char *>(to) + fSoAMemberOffsets[i];
auto begin = ROOT::RRVecField::ResizeRVec(rvecPtr, N, memberSize, memberField, fRecordMemberDeleters[i].get());

if (memberField->IsSimple() && N) {
GetPrincipalColumnOf(*memberField)->ReadV(collectionStart, N, begin);
} else {
for (std::size_t j = 0; j < N; ++j) {
CallReadOn(*memberField, collectionStart + j, begin + (j * memberSize));
}
}
}
}

void ROOT::Experimental::RSoAField::ConstructValue(void *where) const
Expand All @@ -856,10 +883,31 @@ void ROOT::Experimental::RSoAField::RSoADeleter::operator()(void *objPtr, bool d
RDeleter::operator()(objPtr, dtorOnly);
}

std::vector<ROOT::RFieldBase::RValue> ROOT::Experimental::RSoAField::SplitValue(const RValue & /* value */) const
std::vector<ROOT::RFieldBase::RValue> ROOT::Experimental::RSoAField::SplitValue(const RValue &value) const
{
throw RException(R__FAIL("not yet implemented"));
return std::vector<RValue>();
const auto nSoAMembers = fSoAMemberOffsets.size();

{
std::lock_guard<std::mutex> lockGuard(*fLockSplitFields);
if (!fSplitFields) {
fSplitFields = std::make_unique<std::vector<std::unique_ptr<ROOT::RRVecField>>>();
fSplitFields->reserve(nSoAMembers);
for (std::size_t i = 0; i < nSoAMembers; ++i) {
const auto itemField = fRecordMemberFields[i];
fSplitFields->emplace_back(std::make_unique<RRVecField>(itemField->GetFieldName(), itemField->Clone("_0")));
}
}
}

auto valuePtr = value.GetPtr<void>();
auto soaPtr = static_cast<unsigned char *>(valuePtr.get());
std::vector<RValue> values;
values.reserve(nSoAMembers);
for (std::size_t i = 0; i < nSoAMembers; ++i) {
values.emplace_back(
(*fSplitFields)[i]->BindValue(std::shared_ptr<void>(valuePtr, soaPtr + fSoAMemberOffsets[i])));
}
return values;
}

size_t ROOT::Experimental::RSoAField::GetValueSize() const
Expand Down Expand Up @@ -887,6 +935,11 @@ const std::type_info *ROOT::Experimental::RSoAField::GetPolymorphicTypeInfo() co
return fSoAClass->GetTypeInfo();
}

void ROOT::Experimental::RSoAField::AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const
{
visitor.VisitSoAField(*this);
}

//------------------------------------------------------------------------------

ROOT::REnumField::REnumField(std::string_view fieldName, std::string_view enumName)
Expand Down
5 changes: 5 additions & 0 deletions tree/ntuple/src/RFieldVisitor.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,11 @@ void ROOT::Internal::RPrintValueVisitor::VisitRVecField(const ROOT::RRVecField &
PrintCollection(field);
}

void ROOT::Internal::RPrintValueVisitor::VisitSoAField(const ROOT::Experimental::RSoAField &field)
{
PrintRecord(field);
}

//---------------------------- RNTupleFormatter --------------------------------

std::string ROOT::Internal::RNTupleFormatter::FitString(const std::string &str, int availableSpace)
Expand Down
23 changes: 23 additions & 0 deletions tree/ntuple/test/SoAField.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,27 @@ struct SoASimpleWrongMember {
ClassDefNV(SoASimpleWrongMember, 2);
};

/// class with non-trivial constructor and destructor
struct ComplexMember {
inline static int gNCallConstructor = 0;
inline static int gNCallDestructor = 0;

ComplexMember() { gNCallConstructor++; }
~ComplexMember() { gNCallDestructor++; }

ClassDefNV(ComplexMember, 2);
};

struct RecordComplex {
ComplexMember fA;

ClassDefNV(RecordComplex, 2);
};

struct SoAComplex {
ROOT::RVec<ComplexMember> fA;

ClassDefNV(SoAComplex, 2);
};

#endif // ROOT_RNTuple_Test_SoAField
4 changes: 4 additions & 0 deletions tree/ntuple/test/SoAFieldLinkDef.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@
#pragma link C++ options=rntupleSoARecord(RecordSimple) class SoASimpleMissingMember+;
#pragma link C++ options=rntupleSoARecord(RecordSimple) class SoASimpleWrongMember+;

#pragma link C++ class ComplexMember+;
#pragma link C++ class RecordComplex+;
#pragma link C++ options=rntupleSoARecord(RecordComplex) class SoAComplex+;

#endif // __CLING__
Loading