forked from RL-S/Kokkidio
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathViewMap.hpp
373 lines (328 loc) · 10.7 KB
/
ViewMap.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
#ifndef KOKKIDIO_VIEWMAP_HPP
#define KOKKIDIO_VIEWMAP_HPP
#ifndef KOKKIDIO_PUBLIC_HEADER
#error "Do not include this file directly. Include Kokkidio/Core.hpp instead."
#endif
#include "Kokkidio/TargetSpaces.hpp"
#include "Kokkidio/typeAliases.hpp"
#include "Kokkidio/util.hpp"
#include "Kokkidio/EigenTypeHelpers.hpp"
#include "Kokkidio/memory.hpp"
#include "Kokkidio/syclify_macros.hpp"
#include <Kokkos_Core.hpp>
namespace Kokkidio
{
template<typename _EigenType, Target targetArg = DefaultTarget>
class ViewMap {
public:
static constexpr Target target { ExecutionTarget<targetArg> };
using EigenType_host = _EigenType;
/* To make the ViewMap work, the device view must be non-const in most
* cases, to not end up with inaccessible device memory.
* However, if the target is the host, then a cast to non-const
* could allow write access to a const object.
* Instead, any allocations and copies are simply skipped in that case.
* */
using EigenType_target = std::conditional_t<target == Target::host,
EigenType_host,
std::remove_const_t<EigenType_host>
>;
using ThisType = ViewMap<EigenType_target, target>;
using MemorySpace = Kokkidio::MemorySpace <target>;
using ExecutionSpace = Kokkidio::ExecutionSpace<target>;
private:
using ViewTypeStruct = Kokkidio::detail::ViewType<EigenType_target, MemorySpace>;
public:
using ViewType = typename ViewTypeStruct::Type;
using Scalar = typename ViewTypeStruct::Scalar;
using HostMirror = typename ViewType::HostMirror;
using MapType = Eigen::Map<EigenType_host>;
static_assert( is_contiguous<EigenType_target>() );
protected:
ViewType m_view;
observer_ptr<EigenType_host> m_obj {nullptr};
public:
/* For fixed size Eigen types,
* the default constructor allocates memory,
* while for dynamically sized Eigen objects, it does nothing,
* like in Eigen itself. */
ViewMap(){
/* ViewMap(Index, Index) overwrites rows/cols
* if they're known at compile time,
* so we could pass any numbers. */
using P = EigenType_host;
if constexpr ( P::SizeAtCompileTime != Eigen::Dynamic ){
this->allocView(P::RowsAtCompileTime, P::ColsAtCompileTime);
}
}
ViewMap(Index rows, Index cols){
this->allocView(rows, cols);
}
/* For Eigen vector types,
* we allow a single size parameter, like in Eigen itself. */
template<typename P = EigenType_host,
typename std::enable_if_t<P::IsVectorAtCompileTime, int> = 0>
ViewMap(Index size) :
/* ViewMap(Index, Index) overwrites rows/cols
* if they're known at compile time,
* so we could pass any numbers. */
ViewMap(size, size)
{
static_assert( std::is_same_v<P, EigenType_host> );
}
ViewMap( EigenType_host& hostObj ){
this->wrapOrAlloc(hostObj);
}
/* cannot be called in device code */
void resize(Index rows, Index cols){
this->adjustDims(rows, cols);
if ( rows == this->rows() && cols == this->cols() ){
return;
}
if constexpr (
!std::is_const_v<EigenType_host> &&
!is_eigen_map_v<std::remove_const_t<EigenType_host>>
){
/* If the ViewMap was given a (non-const) object on construction,
* then ViewMap::resize should be the correct way to resize both,
* because the Eigen object and Kokkos::View don't know about each
* other - i.e. there is no other non-manual way.
*/
if (m_obj){
EigenType_host& hostObj { *(this->m_obj) };
/* Resize the host object */
hostObj.resize(rows, cols);
/* and if the target is the host, then the View is unmanaged,
* so we just wrap it around the host object */
if constexpr ( target == Target::host ){
this->wrapView(hostObj);
/* On the device, the View is always managed, so we resize it */
} else {
this->resizeView(rows, cols);
}
/* Without a host object, it's also always a managed View */
} else {
this->resizeView(rows, cols);
}
} else {
static_assert(dependent_false<EigenType_host>::value && false,
"Cannot resize a const or non-owning object!"
);
}
}
void resize(Index size){
static_assert( EigenType_host::IsVectorAtCompileTime );
this->resize(size, size);
}
template<typename EigenObjOrView>
void resizeLike(const EigenObjOrView& obj){
this->resize( obj.rows(), obj.cols() );
}
KOKKOS_FUNCTION
constexpr bool isManaged() const {
/* The View is only unmanaged in one case:
* if a host object was provided during construction AND
* the target (~= ExecutionSpace) is also the host.
* A device View is always managed,
* and so is a host View that doesn't wrap a host object. */
return !(target == Target::host && m_obj);
}
protected:
void adjustRows(Index& rows) const {
using T = EigenType_target;
if constexpr (T::RowsAtCompileTime != Eigen::Dynamic){
rows = T::RowsAtCompileTime;
}
}
void adjustCols(Index& cols) const {
using T = EigenType_target;
if constexpr (T::ColsAtCompileTime != Eigen::Dynamic){
cols = T::ColsAtCompileTime;
}
}
void adjustDims(Index& rows, Index& cols) const {
/* if the Eigen class has fixed size rows/columns,
* overwrite rows/cols */
this->adjustRows(rows);
this->adjustCols(cols);
}
void allocView(Index rows, Index cols){
this->adjustDims(rows, cols);
this->m_view = ViewType{
Kokkos::view_alloc(
MemorySpace{}, Kokkos::WithoutInitializing,
"ViewMap::allocView"
),
static_cast<std::size_t>(rows),
static_cast<std::size_t>(cols)
};
printd( "(%p) Allocating View, on %cPU, size %i x %i.\n"
, (void*) this->m_view.data()
, target == Target::host ? 'C' : 'G'
, static_cast<int>( this->rows() )
, static_cast<int>( this->cols() )
);
if ( !this->m_view.is_allocated() ){
printd( "(%p) View not allocated, on %cPU, size %i x %i.\n"
, (void*) this->m_view.data()
, target == Target::host ? 'C' : 'G'
, static_cast<int>( this->rows() )
, static_cast<int>( this->cols() )
);
}
assert( this->isAlloc() );
}
void resizeView(Index rows, Index cols){
assert( this->isManaged() );
// adjustDims(rows, cols);
if ( rows == this->rows() && cols == this->cols() ){
return;
}
Kokkos::resize(
Kokkos::WithoutInitializing,
this->m_view,
static_cast<std::size_t>(rows),
static_cast<std::size_t>(cols)
);
printd( "(%p) Setting view size to %i.\n", (void*) this, this->size() );
}
void wrapView(EigenType_host& hostObj ){
assert( !this->isManaged() );
printd( "(%p) Creating View from data pointer, target %cPU, size %i x %i.\n"
, (void*) hostObj.data()
, target == Target::host ? 'C' : 'G'
, static_cast<int>( hostObj.rows() )
, static_cast<int>( hostObj.cols() )
);
this->m_view = ViewType{ hostObj.data(),
static_cast<std::size_t>( hostObj.rows() ),
static_cast<std::size_t>( hostObj.cols() )
};
printd( "(%p) View now has size %i x %i.\n"
, (void*) this->view().data()
, static_cast<int>( this->rows() )
, static_cast<int>( this->cols() )
);
}
void wrapOrAlloc( EigenType_host& hostObj ){
if constexpr ( target == Target::host ){
/* If the target is the host, then the memory is already accessible,
* Therefore, we don't need to allocate device memory,
* and can simply wrap the host object's data in an unmanaged view.
* */
this->m_obj = make_observer( &hostObj );
this->wrapView(hostObj);
} else {
this->allocView( hostObj.rows(), hostObj.cols() );
}
}
public:
KOKKOS_FUNCTION
bool isAlloc() const {
// #if defined(KOKKIDIO_DEBUG_OUTPUT)
// if ( !this->m_view.is_allocated() ){
// printd( "(%p) View not allocated, on %cPU, size %i x %i.\n"
// , (void*) this->m_view.data()
// , target == Target::host ? 'C' : 'G'
// , static_cast<int>( this->rows() )
// , static_cast<int>( this->cols() )
// );
// }
// #endif
return this->m_view.is_allocated();
}
// KOKKOS_FUNCTION
// Scalar* data() {
// assert( this->isAlloc() );
// return this->m_view.data();
// }
KOKKOS_FUNCTION
decltype(auto) data() const {
assert( this->isAlloc() );
return this->m_view.data();
}
/**
* @brief Returns an Eigen::Map
* to memory on the ViewMap's \a target
* (Target::host or Target::device).
*
* This represents the core functionality of an ViewMap,
* because this Eigen::Map can be used in any Eigen operation.
*
* If the ViewMap was initialised with an Eigen object \a obj and
* \a target == Target::host, then the Eigen::Map uses the same data
* as that \a obj.
* Otherwise, it uses data of a managed Kokkos::View.
*
* @return MapType
*/
KOKKOS_FUNCTION
auto map() const -> MapType {
/* function must be marked as 'const',
* because the operator() of a lambda is 'const' by default,
* and thus a copy-capturing lambda will capture this class'
* 'this' pointer as const.
* */
// if ( !this->isAlloc() ){
if ( !this->m_view.is_allocated() ){
printd( "(%p) View not allocated, on %cPU, size %i x %i.\n"
, (void*) this->m_view.data()
, target == Target::host ? 'C' : 'G'
, static_cast<int>( this->rows() )
, static_cast<int>( this->cols() )
);
}
// assert( this->isAlloc() );
return { this->m_view.data(), this->rows(), this->cols() };
}
/**
* @brief Returns the stored Kokkos::View.
* If the ViewMap was initialised with an Eigen object \a obj,
* this returns an unmanaged View with the same data pointer as \a obj.
* Otherwise, it returns a managed view, whose memory space is
* Kokkos::DefaultExecutionSpace, if \a target == Target::device, and
* Kokkos::DefaultHostExecutionSpace, if \a target == Target::host.
*
* @return ViewType&
*/
KOKKOS_FUNCTION
auto view() const -> ViewType {
return this->m_view;
}
KOKKOS_FUNCTION
Index rows() const {
return static_cast<Index>( this->m_view.extent(0) );
}
KOKKOS_FUNCTION
Index cols() const {
return static_cast<Index>( this->m_view.extent(1) );
}
KOKKOS_FUNCTION
Index size() const {
return static_cast<Index>( this->m_view.size() );
}
};
// static_assert( std::is_trivially_copyable_v<ViewMap<ArrayXXs, Target::device>> );
template<typename T>
struct is_ViewMap : std::false_type {};
template<typename EigenType, Target target>
struct is_ViewMap<ViewMap<EigenType, target>> : std::true_type {};
template<typename T>
inline constexpr bool is_ViewMap_v = is_ViewMap<T>::value;
template<Target target = DefaultTarget, typename EigenType>
std::enable_if_t<
std::is_base_of_v<Eigen::DenseBase<EigenType>, EigenType>,
ViewMap<EigenType, target>
>
viewMap( EigenType& eigenObj ){
return {eigenObj};
}
#define KOKKIDIO_MAPVIEW_FACTORY \
template<typename EigenType, Target target = DefaultTarget> \
ViewMap<EigenType, target> viewMap
KOKKIDIO_MAPVIEW_FACTORY(){ return {}; }
KOKKIDIO_MAPVIEW_FACTORY(Index vectorSize){ return {vectorSize}; }
KOKKIDIO_MAPVIEW_FACTORY(Index rows, Index cols){ return {rows, cols}; }
#undef KOKKIDIO_MAPVIEW_FACTORY
} // namespace Kokkidio
#endif