Skip to content

Commit 6f85fb2

Browse files
author
leonardo beltrame
committed
New view constructor and deepCopy method for Collections
1 parent e40866a commit 6f85fb2

File tree

10 files changed

+562
-28
lines changed

10 files changed

+562
-28
lines changed

DataFormats/Portable/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ SET_PORTABLEHOSTCOLLECTION_READ_RULES(portabletest::TestHostCollection);
9898
They have no implicit or explicit references to alpaka (neither as part of the class signature nor as part of its name).
9999
This could make it possible to read them back with different portability solutions in the future.
100100
101+
The member function `void deepCopy(ConstView const& view)` copies the content of all scalars and columns from `view`
102+
(pointing to data in host memory and potentially to multiple buffers) into the `PortableHostCollection` contiguous buffer.
103+
See the [`View` section](../../DataFormats/SoATemplate/README.md#view) of [`DataFormats/SoATemplate/README.md`](../../DataFormats/SoATemplate/README.md) for more details.
104+
101105
### `PortableDeviceCollection<T, TDev>`
102106
103107
`PortableDeviceCollection<T, TDev>` is a class template that wraps a SoA type `T` and an alpaka device buffer, which

DataFormats/Portable/interface/PortableHostCollection.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ class PortableHostCollection {
9494
layout.ROOTStreamerCleaner();
9595
}
9696

97+
// Copy column by column the content of the given view into this PortableHostCollection.
98+
// The view must point to data in host memory.
99+
void deepCopy(ConstView const& view) { layout_.deepCopy(view); }
100+
97101
private:
98102
std::optional<Buffer> buffer_; //!
99103
Layout layout_; //

DataFormats/Portable/test/BuildFile.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
<use name="DataFormats/Portable"/>
33
<use name="DataFormats/SoATemplate"/>
44
<use name="catch2"/>
5+
<use name="eigen"/>
56
</bin>
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#include <Eigen/Core>
2+
#include <Eigen/Dense>
3+
4+
#include <catch.hpp>
5+
6+
#include "DataFormats/Portable/interface/PortableHostCollection.h"
7+
#include "DataFormats/SoATemplate/interface/SoACommon.h"
8+
#include "DataFormats/SoATemplate/interface/SoALayout.h"
9+
#include "DataFormats/SoATemplate/interface/SoAView.h"
10+
11+
GENERATE_SOA_LAYOUT(SoAPositionTemplate,
12+
SOA_COLUMN(float, x),
13+
SOA_COLUMN(float, y),
14+
SOA_COLUMN(float, z),
15+
SOA_SCALAR(int, detectorType))
16+
17+
using SoAPosition = SoAPositionTemplate<>;
18+
using SoAPositionView = SoAPosition::View;
19+
using SoAPositionConstView = SoAPosition::ConstView;
20+
21+
GENERATE_SOA_LAYOUT(SoAPCATemplate,
22+
SOA_COLUMN(float, eigenvalues),
23+
SOA_COLUMN(float, eigenvector_1),
24+
SOA_COLUMN(float, eigenvector_2),
25+
SOA_COLUMN(float, eigenvector_3),
26+
SOA_EIGEN_COLUMN(Eigen::Vector3d, candidateDirection))
27+
28+
using SoAPCA = SoAPCATemplate<>;
29+
using SoAPCAView = SoAPCA::View;
30+
using SoAPCAConstView = SoAPCA::ConstView;
31+
32+
GENERATE_SOA_LAYOUT(GenericSoATemplate,
33+
SOA_COLUMN(float, xPos),
34+
SOA_COLUMN(float, yPos),
35+
SOA_COLUMN(float, zPos),
36+
SOA_EIGEN_COLUMN(Eigen::Vector3d, candidateDirection))
37+
38+
using GenericSoA = GenericSoATemplate<>;
39+
using GenericSoAView = GenericSoA::View;
40+
using GenericSoAConstView = GenericSoA::ConstView;
41+
42+
TEST_CASE("Deep copy from SoA Generic View") {
43+
// common number of elements for the SoAs
44+
const std::size_t elems = 10;
45+
46+
// Portable Collections
47+
PortableHostCollection<SoAPosition> positionCollection(elems, cms::alpakatools::host());
48+
PortableHostCollection<SoAPCA> pcaCollection(elems, cms::alpakatools::host());
49+
50+
// Portable Collection Views
51+
SoAPositionView& positionCollectionView = positionCollection.view();
52+
SoAPCAView& pcaCollectionView = pcaCollection.view();
53+
// Portable Collection ConstViews
54+
const SoAPositionConstView& positionCollectionConstView = positionCollection.const_view();
55+
const SoAPCAConstView& pcaCollectionConstView = pcaCollection.const_view();
56+
57+
// fill up
58+
for (size_t i = 0; i < elems; i++) {
59+
positionCollectionView[i] = {i * 1.0f, i * 2.0f, i * 3.0f};
60+
}
61+
positionCollectionView.detectorType() = 1;
62+
63+
float time = 0.01;
64+
for (size_t i = 0; i < elems; i++) {
65+
pcaCollectionView[i].eigenvector_1() = positionCollectionView[i].x() / time;
66+
pcaCollectionView[i].eigenvector_2() = positionCollectionView[i].y() / time;
67+
pcaCollectionView[i].eigenvector_3() = positionCollectionView[i].z() / time;
68+
pcaCollectionView[i].candidateDirection()(0) = positionCollectionView[i].x() / time;
69+
pcaCollectionView[i].candidateDirection()(1) = positionCollectionView[i].y() / time;
70+
pcaCollectionView[i].candidateDirection()(2) = positionCollectionView[i].z() / time;
71+
}
72+
73+
SECTION("Deep copy the View") {
74+
// addresses and size of the SoA columns
75+
const auto posRecs = positionCollectionView.records();
76+
const auto pcaRecs = pcaCollectionView.records();
77+
78+
// building the View with runtime check for the size
79+
GenericSoAView genericView(posRecs.x(), posRecs.y(), posRecs.z(), pcaRecs.candidateDirection());
80+
81+
// Check for equality of memory addresses
82+
REQUIRE(genericView.metadata().addressOf_xPos() == positionCollectionView.metadata().addressOf_x());
83+
REQUIRE(genericView.metadata().addressOf_yPos() == positionCollectionView.metadata().addressOf_y());
84+
REQUIRE(genericView.metadata().addressOf_zPos() == positionCollectionView.metadata().addressOf_z());
85+
REQUIRE(genericView.metadata().addressOf_candidateDirection() ==
86+
pcaCollectionView.metadata().addressOf_candidateDirection());
87+
88+
// PortableHostCollection that will host the aggregated columns
89+
PortableHostCollection<GenericSoA> genericCollection(elems, cms::alpakatools::host());
90+
genericCollection.deepCopy(genericView);
91+
92+
// Check for inequality of memory addresses
93+
REQUIRE(genericCollection.view().metadata().addressOf_xPos() != positionCollectionView.metadata().addressOf_x());
94+
REQUIRE(genericCollection.view().metadata().addressOf_yPos() != positionCollectionView.metadata().addressOf_y());
95+
REQUIRE(genericCollection.view().metadata().addressOf_zPos() != positionCollectionView.metadata().addressOf_z());
96+
REQUIRE(genericCollection.view().metadata().addressOf_candidateDirection() !=
97+
pcaCollectionView.metadata().addressOf_candidateDirection());
98+
}
99+
100+
SECTION("Deep copy the ConstView") {
101+
// addresses and size of the SoA columns
102+
const auto posRecs = positionCollectionConstView.records();
103+
const auto pcaRecs = pcaCollectionConstView.records();
104+
105+
// building the View with runtime check for the size
106+
GenericSoAConstView genericConstView(posRecs.x(), posRecs.y(), posRecs.z(), pcaRecs.candidateDirection());
107+
108+
// Check for equality of memory addresses
109+
REQUIRE(genericConstView.metadata().addressOf_xPos() == positionCollectionView.metadata().addressOf_x());
110+
REQUIRE(genericConstView.metadata().addressOf_yPos() == positionCollectionView.metadata().addressOf_y());
111+
REQUIRE(genericConstView.metadata().addressOf_zPos() == positionCollectionView.metadata().addressOf_z());
112+
REQUIRE(genericConstView.metadata().addressOf_candidateDirection() ==
113+
pcaCollectionView.metadata().addressOf_candidateDirection());
114+
115+
// PortableHostCollection that will host the aggregated columns
116+
PortableHostCollection<GenericSoA> genericCollection(elems, cms::alpakatools::host());
117+
genericCollection.deepCopy(genericConstView);
118+
119+
// Check for inequality of memory addresses
120+
REQUIRE(genericCollection.view().metadata().addressOf_xPos() != positionCollectionView.metadata().addressOf_x());
121+
REQUIRE(genericCollection.view().metadata().addressOf_yPos() != positionCollectionView.metadata().addressOf_y());
122+
REQUIRE(genericCollection.view().metadata().addressOf_zPos() != positionCollectionView.metadata().addressOf_z());
123+
REQUIRE(genericCollection.view().metadata().addressOf_candidateDirection() !=
124+
pcaCollectionView.metadata().addressOf_candidateDirection());
125+
}
126+
}

DataFormats/SoATemplate/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ provided: `ViewTemplate`, `ViewViewTemplateFreeParams` and respectively `ConstVi
4444
`ConstViewTemplateFreeParams`. The parametrization of those templates is explained in the [Template
4545
parameters section](#template-parameters).
4646

47+
It is also possible to build a generic `View` or `ConstView` passing from the [Metarecords sublass](#metarecords-subclass). This
48+
view can point to data belonging to different SoAs and thus not contiguous in memory.
49+
4750
## Metadata subclass
4851

4952
In order to no clutter the namespace of the generated class, a subclass name `Metadata` is generated. It is
@@ -52,6 +55,13 @@ of elements in the SoA), `byteSize()`, `byteAlignment()`, `data()` (a pointer to
5255
function computes the first byte of a structure right after a layout, allowing using a single buffer for multiple
5356
layouts.
5457

58+
## Metarecords subclass
59+
60+
The nested type `Metarecords` describes the elements of the SoA. It can be instantiated by the `records()` member
61+
function of a `View` or `ConstView`. Every object contains the address of the first element of the column, the number
62+
of elements per column, and the stride for the Eigen columns. These are used to validate the columns size at run time
63+
and to build a generic `View` as described in [View](#view).
64+
5565
## ROOT serialization and de-serialization
5666

5767
Layouts can be serialized and de-serialized with ROOT. In order to generate the ROOT dictionary, separate

DataFormats/SoATemplate/interface/SoACommon.h

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#include <cstdint>
99
#include <cassert>
10+
#include <cstring>
11+
#include <memory>
1012
#include <ostream>
1113
#include <tuple>
1214
#include <type_traits>
@@ -123,12 +125,13 @@ namespace cms::soa {
123125
// default constructor
124126
SoAConstParametersImpl() = default;
125127

126-
// constructor from an address
127-
SOA_HOST_DEVICE SOA_INLINE constexpr SoAConstParametersImpl(ValueType const* addr) : addr_(addr) {}
128+
// constructor from address and size
129+
SOA_HOST_DEVICE SOA_INLINE constexpr SoAConstParametersImpl(ValueType const* addr, size_type size)
130+
: addr_(addr), size_{size} {}
128131

129132
// constructor from a non-const parameter set
130133
SOA_HOST_DEVICE SOA_INLINE constexpr SoAConstParametersImpl(SoAParametersImpl<columnType, ValueType> const& o)
131-
: addr_{o.addr_} {}
134+
: addr_{o.addr_}, size_{o.size_} {}
132135

133136
static constexpr bool checkAlignment(ValueType* addr, byte_size_type alignment) {
134137
return reinterpret_cast<intptr_t>(addr) % alignment;
@@ -139,6 +142,7 @@ namespace cms::soa {
139142
public:
140143
// scalar or column
141144
ValueType const* addr_ = nullptr;
145+
size_type size_ = 0;
142146
};
143147

144148
// Templated const parameter specialisation for Eigen columns
@@ -153,17 +157,19 @@ namespace cms::soa {
153157
// default constructor
154158
SoAConstParametersImpl() = default;
155159

156-
// constructor from individual address and stride
157-
SOA_HOST_DEVICE SOA_INLINE constexpr SoAConstParametersImpl(ScalarType const* addr, byte_size_type stride)
158-
: addr_(addr), stride_(stride) {}
160+
// constructor from individual address, stride and size
161+
SOA_HOST_DEVICE SOA_INLINE constexpr SoAConstParametersImpl(ScalarType const* addr,
162+
byte_size_type stride,
163+
size_type size)
164+
: addr_(addr), stride_(stride), size_{size} {}
159165

160166
// constructor from address and stride packed in a tuple
161167
SOA_HOST_DEVICE SOA_INLINE constexpr SoAConstParametersImpl(TupleOrPointerType const& tuple)
162168
: addr_(std::get<0>(tuple)), stride_(std::get<1>(tuple)) {}
163169

164170
// constructor from a non-const parameter set
165171
SOA_HOST_DEVICE SOA_INLINE constexpr SoAConstParametersImpl(SoAParametersImpl<columnType, ValueType> const& o)
166-
: addr_{o.addr_}, stride_{o.stride_} {}
172+
: addr_{o.addr_}, stride_{o.stride_}, size_{o.size_} {}
167173

168174
static constexpr bool checkAlignment(TupleOrPointerType const& tuple, byte_size_type alignment) {
169175
const auto& [addr, stride] = tuple;
@@ -173,9 +179,10 @@ namespace cms::soa {
173179
TupleOrPointerType tupleOrPointer() { return {addr_, stride_}; }
174180

175181
public:
176-
// address and stride
182+
// address, stride and size
177183
ScalarType const* addr_ = nullptr;
178184
byte_size_type stride_ = 0;
185+
size_type size_ = 0;
179186
};
180187

181188
// Matryoshka template to avoid commas inside macros
@@ -200,8 +207,9 @@ namespace cms::soa {
200207
// default constructor
201208
SoAParametersImpl() = default;
202209

203-
// constructor from an address
204-
SOA_HOST_DEVICE SOA_INLINE constexpr SoAParametersImpl(ValueType* addr) : addr_(addr) {}
210+
// constructor from address and size
211+
SOA_HOST_DEVICE SOA_INLINE constexpr SoAParametersImpl(ValueType* addr, size_type size)
212+
: addr_(addr), size_{size} {}
205213

206214
static constexpr bool checkAlignment(ValueType* addr, byte_size_type alignment) {
207215
return reinterpret_cast<intptr_t>(addr) % alignment;
@@ -212,6 +220,7 @@ namespace cms::soa {
212220
public:
213221
// scalar or column
214222
ValueType* addr_ = nullptr;
223+
size_type size_ = 0;
215224
};
216225

217226
// Templated parameter specialisation for Eigen columns
@@ -229,9 +238,9 @@ namespace cms::soa {
229238
// default constructor
230239
SoAParametersImpl() = default;
231240

232-
// constructor from individual address and stride
233-
SOA_HOST_DEVICE SOA_INLINE constexpr SoAParametersImpl(ScalarType* addr, byte_size_type stride)
234-
: addr_(addr), stride_(stride) {}
241+
// constructor from individual address, stride and size
242+
SOA_HOST_DEVICE SOA_INLINE constexpr SoAParametersImpl(ScalarType* addr, byte_size_type stride, size_type size)
243+
: addr_(addr), stride_(stride), size_(size) {}
235244

236245
// constructor from address and stride packed in a tuple
237246
SOA_HOST_DEVICE SOA_INLINE constexpr SoAParametersImpl(TupleOrPointerType const& tuple)
@@ -245,9 +254,10 @@ namespace cms::soa {
245254
TupleOrPointerType tupleOrPointer() { return {addr_, stride_}; }
246255

247256
public:
248-
// address and stride
257+
// address, stride and size
249258
ScalarType* addr_ = nullptr;
250259
byte_size_type stride_ = 0;
260+
size_type size_ = 0;
251261
};
252262

253263
// Matryoshka template to avoid commas inside macros
@@ -268,13 +278,13 @@ namespace cms::soa {
268278
template <SoAColumnType COLUMN_TYPE, typename T>
269279
SOA_HOST_DEVICE SOA_INLINE constexpr SoAParametersImpl<COLUMN_TYPE, T> const_cast_SoAParametersImpl(
270280
SoAConstParametersImpl<COLUMN_TYPE, T> const& o) {
271-
return SoAParametersImpl<COLUMN_TYPE, T>{non_const_ptr(o.addr_)};
281+
return SoAParametersImpl<COLUMN_TYPE, T>{non_const_ptr(o.addr_), o.size_};
272282
}
273283

274284
template <typename T>
275285
SOA_HOST_DEVICE SOA_INLINE constexpr SoAParametersImpl<SoAColumnType::eigen, T> const_cast_SoAParametersImpl(
276286
SoAConstParametersImpl<SoAColumnType::eigen, T> const& o) {
277-
return SoAParametersImpl<SoAColumnType::eigen, T>{non_const_ptr(o.addr_), o.stride_};
287+
return SoAParametersImpl<SoAColumnType::eigen, T>{non_const_ptr(o.addr_), o.stride_, o.size_};
278288
}
279289

280290
// Helper template managing the value at index idx within a column.
@@ -554,6 +564,7 @@ namespace cms::soa {
554564
#endif
555565

556566
// Helper function to compute aligned size
567+
//this is an integer division -> it rounds size to the next multiple of alignment
557568
constexpr inline byte_size_type alignSize(byte_size_type size, byte_size_type alignment) {
558569
return ((size + alignment - 1) / alignment) * alignment;
559570
}
@@ -564,11 +575,12 @@ namespace cms::soa {
564575
#define SOA_COLUMN(TYPE, NAME) (_VALUE_TYPE_COLUMN, TYPE, NAME)
565576
#define SOA_EIGEN_COLUMN(TYPE, NAME) (_VALUE_TYPE_EIGEN_COLUMN, TYPE, NAME)
566577

567-
/* Iterate on the macro MACRO and return the result as a comma separated list */
578+
/* Iterate on the macro MACRO and return the result as a comma separated list, converting
579+
the boost sequence into tuples and then into list */
568580
#define _ITERATE_ON_ALL_COMMA(MACRO, DATA, ...) \
569581
BOOST_PP_TUPLE_ENUM(BOOST_PP_SEQ_TO_TUPLE(_ITERATE_ON_ALL(MACRO, DATA, __VA_ARGS__)))
570582

571-
/* Iterate MACRO on all elements */
583+
/* Iterate MACRO on all elements of the boost sequence */
572584
#define _ITERATE_ON_ALL(MACRO, DATA, ...) BOOST_PP_SEQ_FOR_EACH(MACRO, DATA, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
573585

574586
/* Switch on macros depending on scalar / column type */
@@ -599,6 +611,7 @@ namespace cms::soa {
599611
SOA_HOST_DEVICE SOA_INLINE SoAColumnAccessorsImpl(const SoAParametersImpl<SoAColumnType::column, T>& params)
600612
: params_(params) {}
601613
SOA_HOST_DEVICE SOA_INLINE T* operator()() { return params_.addr_; }
614+
602615
using NoParamReturnType = T*;
603616
using ParamReturnType = T&;
604617
SOA_HOST_DEVICE SOA_INLINE T& operator()(size_type index) { return params_.addr_[index]; }

0 commit comments

Comments
 (0)