Skip to content

Commit 5434b6a

Browse files
committed
[ntuple] use TClass alignment info where possible
Fixes RNTuple alignment of classes whose alignment is determined by a transient member.
1 parent 16182d2 commit 5434b6a

13 files changed

Lines changed: 145 additions & 35 deletions

core/foundation/inc/ROOT/BitUtils.hxx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <algorithm>
1616
#include <cassert>
1717
#include <cstddef>
18+
#include <cstdint>
1819
#include <type_traits>
1920

2021
#ifdef _MSC_VER
@@ -155,6 +156,8 @@ inline std::size_t TrailingZeroes(T x)
155156
#endif // _MSC_VER
156157
}
157158

159+
inline bool IsPowerOfTwo(std::uint64_t v) { return (v & (v - 1)) == 0; }
160+
158161
} // namespace Internal
159162
} // namespace ROOT
160163

tree/ntuple/inc/ROOT/RField.hxx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,6 @@ private:
165165
TClass *fClass;
166166
/// Additional information kept for each entry in `fSubfields`
167167
std::vector<RSubfieldInfo> fSubfieldsInfo;
168-
std::size_t fMaxAlignment = 1;
169168

170169
/// The staging area stores inputs to I/O rules according to the offsets given by the streamer info of
171170
/// "TypeName@@Version". The area is allocated depending on I/O rules resp. the source members of the I/O rules.
@@ -220,8 +219,8 @@ public:
220219
~RClassField() override;
221220

222221
std::vector<RValue> SplitValue(const RValue &value) const final;
223-
size_t GetValueSize() const final;
224-
size_t GetAlignment() const final { return fMaxAlignment; }
222+
std::size_t GetValueSize() const final;
223+
std::size_t GetAlignment() const final;
225224
std::uint32_t GetTypeVersion() const final;
226225
std::uint32_t GetTypeChecksum() const final;
227226
/// For polymorphic classes (that declare or inherit at least one virtual method), return the expected dynamic type

tree/ntuple/inc/ROOT/RField/RFieldProxiedCollection.hxx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ public:
176176
~RProxiedCollectionField() override = default;
177177

178178
std::vector<RValue> SplitValue(const RValue &value) const final;
179-
size_t GetValueSize() const final { return fProxy->Sizeof(); }
180-
size_t GetAlignment() const final { return alignof(std::max_align_t); }
179+
std::size_t GetValueSize() const final;
180+
std::size_t GetAlignment() const final;
181181
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
182182
};
183183

tree/ntuple/inc/ROOT/RField/RFieldSTLMisc.hxx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,8 @@ public:
264264
~ROptionalField() override = default;
265265

266266
std::vector<RValue> SplitValue(const RValue &value) const final;
267-
size_t GetValueSize() const final;
268-
size_t GetAlignment() const final;
267+
std::size_t GetValueSize() const final;
268+
std::size_t GetAlignment() const final;
269269
};
270270

271271
template <typename ItemT>
@@ -313,8 +313,8 @@ public:
313313
~RUniquePtrField() override = default;
314314

315315
std::vector<RValue> SplitValue(const RValue &value) const final;
316-
size_t GetValueSize() const final { return sizeof(std::unique_ptr<char>); }
317-
size_t GetAlignment() const final { return alignof(std::unique_ptr<char>); }
316+
std::size_t GetValueSize() const final { return sizeof(std::unique_ptr<char>); }
317+
std::size_t GetAlignment() const final { return alignof(std::unique_ptr<char>); }
318318
};
319319

320320
template <typename ItemT>
@@ -363,8 +363,8 @@ public:
363363
RField &operator=(RField &&other) = default;
364364
~RField() final = default;
365365

366-
size_t GetValueSize() const final { return sizeof(std::string); }
367-
size_t GetAlignment() const final { return std::alignment_of<std::string>(); }
366+
std::size_t GetValueSize() const final { return sizeof(std::string); }
367+
std::size_t GetAlignment() const final { return alignof(std::string); }
368368
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
369369
};
370370

@@ -436,8 +436,8 @@ public:
436436
RVariantField &operator=(RVariantField &&other) = default;
437437
~RVariantField() override = default;
438438

439-
size_t GetValueSize() const final;
440-
size_t GetAlignment() const final;
439+
std::size_t GetValueSize() const final;
440+
std::size_t GetAlignment() const final;
441441
};
442442

443443
template <typename... ItemTs>

tree/ntuple/inc/ROOT/RField/RFieldSequenceContainer.hxx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,8 @@ public:
261261
CreateUntyped(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField);
262262

263263
std::vector<RValue> SplitValue(const RValue &value) const final;
264-
size_t GetValueSize() const final { return sizeof(std::vector<char>); }
265-
size_t GetAlignment() const final { return std::alignment_of<std::vector<char>>(); }
264+
std::size_t GetValueSize() const final;
265+
std::size_t GetAlignment() const final;
266266
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
267267
};
268268

@@ -315,8 +315,8 @@ public:
315315

316316
std::vector<RValue> SplitValue(const RValue &value) const final;
317317

318-
size_t GetValueSize() const final { return sizeof(std::vector<bool>); }
319-
size_t GetAlignment() const final { return std::alignment_of<std::vector<bool>>(); }
318+
std::size_t GetValueSize() const final { return sizeof(std::vector<bool>); }
319+
std::size_t GetAlignment() const final { return alignof(std::vector<bool>); }
320320
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;
321321
};
322322

@@ -410,8 +410,8 @@ public:
410410
RArrayAsVectorField &operator=(RArrayAsVectorField &&other) = default;
411411
~RArrayAsVectorField() final = default;
412412

413-
size_t GetValueSize() const final { return sizeof(std::vector<char>); }
414-
size_t GetAlignment() const final { return std::alignment_of<std::vector<char>>(); }
413+
std::size_t GetValueSize() const final;
414+
std::size_t GetAlignment() const final;
415415

416416
std::vector<RFieldBase::RValue> SplitValue(const RFieldBase::RValue &value) const final;
417417
void AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const final;

tree/ntuple/inc/ROOT/RField/RFieldSoA.hxx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ class RSoAField : public RFieldBase {
6767
/// The offset of the RVec members in the SoA type in the order of subfields of the underlying record type.
6868
/// In particular, the order is not necessarily the same then the order of RVec members in the SoA class.
6969
std::vector<std::size_t> fSoAMemberOffsets;
70-
std::size_t fMaxAlignment = 1;
7170
ROOT::Internal::RColumnIndex fNWritten;
7271

7372
RSoAField(std::string_view fieldName, const RSoAField &source); ///< Used by CloneImpl
@@ -98,7 +97,7 @@ public:
9897

9998
std::vector<RValue> SplitValue(const RValue &value) const final;
10099
size_t GetValueSize() const final;
101-
size_t GetAlignment() const final { return fMaxAlignment; }
100+
size_t GetAlignment() const final;
102101
std::uint32_t GetTypeVersion() const final;
103102
std::uint32_t GetTypeChecksum() const final;
104103
/// For polymorphic classes (that declare or inherit at least one virtual method), return the expected dynamic type

tree/ntuple/inc/ROOT/RFieldBase.hxx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,9 @@ protected:
556556
const ROOT::RNTupleDescriptor *desc, ROOT::DescriptorId_t fieldId);
557557

558558
public:
559+
/// Maximum supported alignment for field types, i.e. maximum returned by GetAlignment()
560+
static constexpr std::size_t kMaxAlignment = 4096;
561+
559562
template <bool IsConstT>
560563
class RSchemaIteratorTemplate;
561564
using RSchemaIterator = RSchemaIteratorTemplate<false>;
@@ -624,10 +627,9 @@ public:
624627
/// correct `std::variant` or all the elements of a collection. The default implementation assumes no subvalues
625628
/// and returns an empty vector.
626629
virtual std::vector<RValue> SplitValue(const RValue &value) const;
627-
/// The number of bytes taken by a value of the appropriate type
630+
/// What sizeof(T) for this type returns
628631
virtual size_t GetValueSize() const = 0;
629-
/// As a rule of thumb, the alignment is equal to the size of the type. There are, however, various exceptions
630-
/// to this rule depending on OS and CPU architecture. So enforce the alignment to be explicitly spelled out.
632+
/// What alignof(T) for this type returns
631633
virtual size_t GetAlignment() const = 0;
632634
std::uint32_t GetTraits() const { return fTraits; }
633635
bool HasReadCallbacks() const { return !fReadCallbacks.empty(); }

tree/ntuple/src/RFieldMeta.cxx

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
// - RField<TObject>
1616
// - RVariantField
1717

18+
#include <ROOT/BitUtils.hxx>
1819
#include <ROOT/RField.hxx>
1920
#include <ROOT/RFieldBase.hxx>
2021
#include <ROOT/RFieldUtils.hxx>
@@ -117,6 +118,12 @@ TEnum *EnsureValidEnum(std::string_view enumName)
117118
return e;
118119
}
119120

121+
void EnsureValidAlignment(std::size_t align)
122+
{
123+
if (align == 0 || align > ROOT::RFieldBase::kMaxAlignment || !ROOT::Internal::IsPowerOfTwo(align))
124+
throw ROOT::RException(R__FAIL(std::string("invalid alignment: ") + std::to_string(align)));
125+
}
126+
120127
/// Create a comma-separated list of type names from the given fields. Uses either the real type names or the
121128
/// type aliases (if there are any, otherwise the actual type name). Used to construct template argument lists
122129
/// for templated types such as std::pair<...>, std::tuple<...>, std::variant<...>.
@@ -187,8 +194,7 @@ std::string BuildMapTypeName(ROOT::RMapField::EMapType mapType, const ROOT::RFie
187194
ROOT::RClassField::RClassField(std::string_view fieldName, const RClassField &source)
188195
: ROOT::RFieldBase(fieldName, source.GetTypeName(), ROOT::ENTupleStructure::kRecord, false /* isSimple */),
189196
fClass(source.fClass),
190-
fSubfieldsInfo(source.fSubfieldsInfo),
191-
fMaxAlignment(source.fMaxAlignment)
197+
fSubfieldsInfo(source.fSubfieldsInfo)
192198
{
193199
for (const auto &f : source.GetConstSubfields()) {
194200
RFieldBase::Attach(f->Clone(f->GetFieldName()));
@@ -281,7 +287,6 @@ ROOT::RClassField::~RClassField()
281287

282288
void ROOT::RClassField::Attach(std::unique_ptr<RFieldBase> child, RSubfieldInfo info)
283289
{
284-
fMaxAlignment = std::max(fMaxAlignment, child->GetAlignment());
285290
fSubfieldsInfo.push_back(info);
286291
RFieldBase::Attach(std::move(child));
287292
}
@@ -623,11 +628,18 @@ std::vector<ROOT::RFieldBase::RValue> ROOT::RClassField::SplitValue(const RValue
623628
return result;
624629
}
625630

626-
size_t ROOT::RClassField::GetValueSize() const
631+
std::size_t ROOT::RClassField::GetValueSize() const
627632
{
628633
return fClass->GetClassSize();
629634
}
630635

636+
std::size_t ROOT::RClassField::GetAlignment() const
637+
{
638+
const auto align = fClass->GetClassAlignment();
639+
EnsureValidAlignment(align);
640+
return align;
641+
}
642+
631643
std::uint32_t ROOT::RClassField::GetTypeVersion() const
632644
{
633645
return fClass->GetClassVersion();
@@ -657,8 +669,7 @@ void ROOT::RClassField::AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) cons
657669
ROOT::Experimental::RSoAField::RSoAField(std::string_view fieldName, const RSoAField &source)
658670
: ROOT::RFieldBase(fieldName, source.GetTypeName(), ROOT::ENTupleStructure::kCollection, false /* isSimple */),
659671
fSoAClass(source.fSoAClass),
660-
fSoAMemberOffsets(source.fSoAMemberOffsets),
661-
fMaxAlignment(source.fMaxAlignment)
672+
fSoAMemberOffsets(source.fSoAMemberOffsets)
662673
{
663674
fTraits = source.GetTraits();
664675
Attach(source.fSubfields[0]->Clone(source.fSubfields[0]->GetFieldName()));
@@ -757,8 +768,6 @@ ROOT::Experimental::RSoAField::RSoAField(std::string_view fieldName, TClass *clS
757768
leftType + " vs. " + rightType + ")"));
758769
}
759770

760-
fMaxAlignment = std::max(fMaxAlignment, vecField->GetAlignment());
761-
762771
assert(itr->second < fSoAMemberOffsets.size());
763772
fSoAMemberOffsets[itr->second] = dataMember->GetOffset();
764773
nMembers++;
@@ -863,7 +872,7 @@ std::vector<ROOT::RFieldBase::RValue> ROOT::Experimental::RSoAField::SplitValue(
863872
return std::vector<RValue>();
864873
}
865874

866-
size_t ROOT::Experimental::RSoAField::GetValueSize() const
875+
std::size_t ROOT::Experimental::RSoAField::GetValueSize() const
867876
{
868877
return fSoAClass->GetClassSize();
869878
}
@@ -878,6 +887,13 @@ std::uint32_t ROOT::Experimental::RSoAField::GetTypeChecksum() const
878887
return fSoAClass->GetCheckSum();
879888
}
880889

890+
std::size_t ROOT::Experimental::RSoAField::GetAlignment() const
891+
{
892+
const auto align = fSoAClass->GetClassAlignment();
893+
EnsureValidAlignment(align);
894+
return align;
895+
}
896+
881897
const std::type_info *ROOT::Experimental::RSoAField::GetPolymorphicTypeInfo() const
882898
{
883899
// TODO(jblomer): factor out
@@ -1198,6 +1214,18 @@ std::vector<ROOT::RFieldBase::RValue> ROOT::RProxiedCollectionField::SplitValue(
11981214
return result;
11991215
}
12001216

1217+
std::size_t ROOT::RProxiedCollectionField::GetValueSize() const
1218+
{
1219+
return fProxy->Sizeof();
1220+
}
1221+
1222+
std::size_t ROOT::RProxiedCollectionField::GetAlignment() const
1223+
{
1224+
const auto align = fProxy->GetCollectionClass()->GetClassAlignment();
1225+
EnsureValidAlignment(align);
1226+
return align;
1227+
}
1228+
12011229
void ROOT::RProxiedCollectionField::AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const
12021230
{
12031231
visitor.VisitProxiedCollectionField(*this);
@@ -1405,7 +1433,9 @@ ROOT::RExtraTypeInfoDescriptor ROOT::RStreamerField::GetExtraTypeInfo() const
14051433

14061434
std::size_t ROOT::RStreamerField::GetAlignment() const
14071435
{
1408-
return std::min(alignof(std::max_align_t), GetValueSize()); // TODO(jblomer): fix me
1436+
const auto align = fClass->GetClassAlignment();
1437+
EnsureValidAlignment(align);
1438+
return align;
14091439
}
14101440

14111441
std::size_t ROOT::RStreamerField::GetValueSize() const

tree/ntuple/src/RFieldSequenceContainer.cxx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
/// \author Jonas Hahnfeld <jonas.hahnfeld@cern.ch>
44
/// \date 2024-11-19
55

6+
#include <ROOT/BitUtils.hxx>
67
#include <ROOT/RField.hxx>
78
#include <ROOT/RFieldBase.hxx>
89
#include <ROOT/RFieldVisitor.hxx>
@@ -30,6 +31,20 @@ std::vector<ROOT::RFieldBase::RValue> SplitVector(std::shared_ptr<void> valuePtr
3031
return result;
3132
}
3233

34+
std::size_t GetSizeOfVector()
35+
{
36+
static_assert(sizeof(std::vector<char>) ==
37+
sizeof(std::vector<ROOT::Internal::RAlignedStorage<ROOT::RFieldBase::kMaxAlignment>>));
38+
return sizeof(std::vector<char>);
39+
}
40+
41+
std::size_t GetAlignOfVector()
42+
{
43+
static_assert(alignof(std::vector<char>) ==
44+
alignof(std::vector<ROOT::Internal::RAlignedStorage<ROOT::RFieldBase::kMaxAlignment>>));
45+
return alignof(std::vector<char>);
46+
}
47+
3348
} // anonymous namespace
3449

3550
ROOT::RArrayField::RArrayField(std::string_view fieldName, std::unique_ptr<RFieldBase> itemField,
@@ -648,6 +663,16 @@ std::vector<ROOT::RFieldBase::RValue> ROOT::RVectorField::SplitValue(const RValu
648663
return SplitVector(value.GetPtr<void>(), *fSubfields[0]);
649664
}
650665

666+
std::size_t ROOT::RVectorField::GetValueSize() const
667+
{
668+
return GetSizeOfVector();
669+
}
670+
671+
std::size_t ROOT::RVectorField::GetAlignment() const
672+
{
673+
return GetAlignOfVector();
674+
}
675+
651676
void ROOT::RVectorField::AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const
652677
{
653678
visitor.VisitVectorField(*this);
@@ -974,6 +999,16 @@ std::vector<ROOT::RFieldBase::RValue> ROOT::RArrayAsVectorField::SplitValue(cons
974999
return SplitVector(value.GetPtr<void>(), *fSubfields[0]);
9751000
}
9761001

1002+
std::size_t ROOT::RArrayAsVectorField::GetValueSize() const
1003+
{
1004+
return GetSizeOfVector();
1005+
}
1006+
1007+
std::size_t ROOT::RArrayAsVectorField::GetAlignment() const
1008+
{
1009+
return GetAlignOfVector();
1010+
}
1011+
9771012
void ROOT::RArrayAsVectorField::AcceptVisitor(ROOT::Detail::RFieldVisitor &visitor) const
9781013
{
9791014
visitor.VisitArrayAsVectorField(*this);

tree/ntuple/test/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ ROOT_STANDARD_LIBRARY_PACKAGE(CustomStruct
1111
HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/CustomStruct.hxx
1212
SOURCES CustomStruct.cxx
1313
LINKDEF CustomStructLinkDef.h
14-
DEPENDENCIES RIO)
14+
DEPENDENCIES RIO ROOTVecOps)
1515
configure_file(CustomStruct.hxx . COPYONLY)
1616
if(MSVC AND NOT CMAKE_GENERATOR MATCHES Ninja)
1717
add_custom_command(TARGET CustomStruct POST_BUILD

0 commit comments

Comments
 (0)