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

[ntuple] Map integral types to fixed width equivalents #16039

Merged
merged 7 commits into from
Jul 25, 2024

Conversation

hahnjo
Copy link
Member

@hahnjo hahnjo commented Jul 16, 2024

This already used to work for some types, such as int, which happen to have the same underlying storage as a fixed width type. This change adds a general RIntegralTypeMap with specializations for standard integer types. As a result, RNTuple gains support for (unsigned) long long on Linux, which is now mapped to [u]int64_t.

Copy link

github-actions bot commented Jul 16, 2024

Test Results

    13 files      13 suites   2d 20h 45m 59s ⏱️
 2 677 tests  2 677 ✅ 0 💤 0 ❌
32 708 runs  32 708 ✅ 0 💤 0 ❌

Results for commit a764b19.

♻️ This comment has been updated with latest results.

@hahnjo
Copy link
Member Author

hahnjo commented Jul 19, 2024

(no changes yet, just to test that the incremental CI is now properly fixed for PRs)

@hahnjo hahnjo marked this pull request as ready for review July 22, 2024 10:00
@hahnjo
Copy link
Member Author

hahnjo commented Jul 22, 2024

Seems to work, all tests pass --> ready for review 😃

@hahnjo hahnjo force-pushed the ntuple-RIntegralField branch 2 times, most recently from 0f885e1 to d607160 Compare July 23, 2024 14:18
Copy link
Contributor

@jblomer jblomer left a comment

Choose a reason for hiding this comment

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

In principle LGTM. Only question about the use of RIntegralField in the visitor.

virtual void VisitInt16Field(const RField<std::int16_t> &field) { VisitField(field); }
virtual void VisitInt32Field(const RField<std::int32_t> &field) { VisitField(field); }
virtual void VisitInt64Field(const RField<std::int64_t> &field) { VisitField(field); }
virtual void VisitInt8Field(const RIntegralField<std::int8_t> &field) { VisitField(field); }
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't we pass an RField<integer type> here and for the others? RIntegralField is an implementation details, isn't it?

Copy link
Member Author

Choose a reason for hiding this comment

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

So I agreed yesterday evening, but when looking into it I remembered why I decided to pass RIntegralField: the solution proposed in this PR builds on a surjective, but not injective function that maps any integral type (the T in RField<T>) onto one of the (fewer) fixed width integer types (the T in RIntegralField, mapped via RIntegralTypeMap). We could continue to pass RFields, but we wouldn't know which of RField<long>, RField<long long> and RField<std::int64_t> to implement in the interface (Linux maps typedef long int64_t; while macOS does typedef long long int64_t;).

In practical terms it would also not be nice to implement because AcceptVisitor is currently a part of RIntegralField, where *this doesn't have type RField<T>. We would either have to add dynamic casting in there, or move AcceptVisitor to the templated RField<T>. That in turn would again require specializations because the name of the Visit function depends on the type...

@@ -80,7 +83,7 @@ class RFieldProvider : public RProvider {
}

template<typename T>
void FillHistogram(const RField<T> &field)
void FillHistogramImpl(const ROOT::Experimental::RFieldBase &field, ROOT::Experimental::RNTupleView<T, false> &view)
Copy link
Contributor

Choose a reason for hiding this comment

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

We should not need to change this, I think.

Copy link
Member Author

Choose a reason for hiding this comment

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

Based on my other comment, I think we are forced to change this... 😐

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, let's not forget the follow-up from your comment to remove the tainting of the ROOT namespace

hahnjo and others added 7 commits July 24, 2024 09:27
It was the only type translation for the "signed" modifier. If this
is needed, it should probably be handled programmatically in
GetNormalizedTypeName().
In order to map standard integer types to fixed width equivalents,
we want to introduce a C++ type mapping which requires one level of
template indirection. For the moment, RField<T> just inherits from
RIntegralType<T> for integral types T.

Co-authored-by: Florine de Geus <[email protected]>
This already used to work for some types, such as int, which happen
to have the same underlying storage as a fixed width type. This
change adds a general RIntegralTypeMap with specializations for
standard integer types. As a result, RNTuple gains support for
(unsigned) long long on Linux, which is now mapped to [u]int64_t.

Co-authored-by: Florine de Geus <[email protected]>
Remove or change entries from type translation map and explicitly
instantiate RField<T> for standard integer types, which will be
mapped to RIntegralType using RIntegralTypeMap since the last commit.

Co-authored-by: Florine de Geus <[email protected]>
This is important because the call to RColumn::Map[V] must use the
fixed width integer type, but the returned pointer type must match
the (unmapped) RField type. Test this via RNTupleView's of standard
integer types.
@hahnjo
Copy link
Member Author

hahnjo commented Jul 24, 2024

I only did two minor changes to the existing commits, as discussed yesterday evening:

diff --git a/tree/ntuple/v7/inc/ROOT/RField.hxx b/tree/ntuple/v7/inc/ROOT/RField.hxx
index 7c9ccebeaa..411ef5733c 100644
--- a/tree/ntuple/v7/inc/ROOT/RField.hxx
+++ b/tree/ntuple/v7/inc/ROOT/RField.hxx
@@ -2179,9 +2179,8 @@ public:
 
 template <typename T>
 class RIntegralField {
-   // Cannot say static_assert(false) because not all compilers implement CWG2518 yet...
-   static_assert(std::is_integral_v<T>, "RIntegralField requires integral type");
-   static_assert(!std::is_integral_v<T>, "unsupported integral type");
+   // Instantiating this base template definition should never happen and is an error!
+   RIntegralField() = delete;
 };
 
 template <>
@@ -2573,6 +2572,8 @@ template <typename T>
 class RField<T, typename std::enable_if<std::is_integral_v<T>>::type> final
    : public RIntegralField<typename Internal::RIntegralTypeMap<T>::type> {
    using MappedType = typename Internal::RIntegralTypeMap<T>::type;
+   static_assert(sizeof(T) == sizeof(MappedType), "invalid size of mapped type");
+   static_assert(std::is_signed_v<T> == std::is_signed_v<MappedType>, "invalid signedness of mapped type");
 
 public:
    RField(std::string_view name) : RIntegralField<MappedType>(name) {}

Eventually, after implementing the changes, I decided to hold off moving some member functions to the templated RField specialization: It didn't work for the Map[V] functions because RColumn::Map[V] has to be called with the fixed width integer type after mapping. Instead the newly added last commit reinterpret_casts the pointer since we can guarantee that the mapped type has identical storage layout. This solves the problem with RNTupleViews, discussed on Mattermost. I still plan to come back to possible deduplications and simplifications in a follow-up PR (now #16101).

@hahnjo hahnjo merged commit 08b97d1 into root-project:master Jul 25, 2024
17 of 18 checks passed
@hahnjo hahnjo deleted the ntuple-RIntegralField branch July 25, 2024 05:37
enirolf added a commit to enirolf/root that referenced this pull request Aug 12, 2024
With the addition of `RIntegralTypeMap` (PR root-project#16039), most of the type
name deduction for type-erased fields was moved from the type
translation map to `RField`'s template specialization. Upon creation of
type-erased STL map(-like) types, originally only the type translation
map was used. This now can cause issues where the map's item types can
be non-normalized. By first explicitly creating fields for the items and
using the type names from those fields, this issue is resolved.
enirolf added a commit to enirolf/root that referenced this pull request Aug 13, 2024
With the addition of `RIntegralTypeMap` (PR root-project#16039), most of the type
name deduction for type-erased fields was moved from the type
translation map to `RField`'s template specialization. Upon creation of
type-erased STL map(-like) types, originally only the type translation
map was used. This now can cause issues where the map's item types can
be non-normalized. By first explicitly creating fields for the items and
using the type names from those fields, this issue is resolved.
enirolf added a commit to enirolf/root that referenced this pull request Aug 14, 2024
With the addition of `RIntegralTypeMap` (PR root-project#16039), most of the type
name deduction for type-erased fields was moved from the type
translation map to `RField`'s template specialization. Upon creation of
type-erased STL map(-like) types, originally only the type translation
map was used. This now can cause issues where the map's item types may
be non-normalized. By using the type names of the subfields of the
`std::pair` item field, we ensure the correct inner type names are used.
enirolf added a commit to enirolf/root that referenced this pull request Aug 16, 2024
With the addition of `RIntegralTypeMap` (PR root-project#16039), most of the type
name deduction for type-erased fields was moved from the type
translation map to `RField`'s template specialization. Upon creation of
type-erased STL map(-like) types, originally only the type translation
map was used. This now can cause issues where the map's item types may
be non-normalized. By using the type names of the subfields of the
`std::pair` item field, we ensure the correct inner type names are used.
enirolf added a commit to enirolf/root that referenced this pull request Aug 16, 2024
With the addition of `RIntegralTypeMap` (PR root-project#16039), most of the type
name deduction for type-erased fields was moved from the type
translation map to `RField`'s template specialization. Upon creation of
type-erased STL map(-like) types, originally only the type translation
map was used. This now can cause issues where the map's item types may
be non-normalized. By using the type names of the subfields of the
`std::pair` item field, we ensure the correct inner type names are used.
enirolf added a commit to enirolf/root that referenced this pull request Aug 16, 2024
With the addition of `RIntegralTypeMap` (PR root-project#16039), most of the type
name deduction for type-erased fields was moved from the type
translation map to `RField`'s template specialization. Upon creation of
type-erased STL map(-like) types, originally only the type translation
map was used. This now can cause issues where the map's item types may
be non-normalized. By using the type names of the subfields of the
`std::pair` item field, we ensure the correct inner type names are used.
enirolf added a commit to enirolf/root that referenced this pull request Aug 26, 2024
With the addition of `RIntegralTypeMap` (PR root-project#16039), most of the type
name deduction for type-erased fields was moved from the type
translation map to `RField`'s template specialization. Upon creation of
type-erased STL map(-like) types, originally only the type translation
map was used. This now can cause issues where the map's item types may
be non-normalized. By using the type names of the subfields of the
`std::pair` item field, we ensure the correct inner type names are used.
enirolf added a commit that referenced this pull request Aug 29, 2024
With the addition of `RIntegralTypeMap` (PR #16039), most of the type
name deduction for type-erased fields was moved from the type
translation map to `RField`'s template specialization. Upon creation of
type-erased STL map(-like) types, originally only the type translation
map was used. This now can cause issues where the map's item types may
be non-normalized. By using the type names of the subfields of the
`std::pair` item field, we ensure the correct inner type names are used.
DuesselbergAdrian pushed a commit to DuesselbergAdrian/root that referenced this pull request Aug 29, 2024
With the addition of `RIntegralTypeMap` (PR root-project#16039), most of the type
name deduction for type-erased fields was moved from the type
translation map to `RField`'s template specialization. Upon creation of
type-erased STL map(-like) types, originally only the type translation
map was used. This now can cause issues where the map's item types may
be non-normalized. By using the type names of the subfields of the
`std::pair` item field, we ensure the correct inner type names are used.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants