Skip to content

Commit 91235b5

Browse files
AdrianoDeeAdriano Di Florioborzarinothingface0
authored andcommitted
Implement PortableObject types
PortableHostObject<T> is a class template that wraps a struct type T and an alpaka host buffer, that owns the memory where the struct is allocated. The content of the struct is persistent, while the buffer itself is transient. Specialisations of this template can be persisted, but requre special ROOT read rules to read the data into an alpaka memory buffer. PortableDeviceObject<T, TDev> is a class template that wraps a struct type T and an alpaka device buffer, that owns the memory where the struct is allocated. To avoid confusion and ODR-violations, the PortableDeviceObject<T, TDev> template cannot be used with the Host device type. Specialisations of this template are transient and cannot be persisted. Add TestHostObject and TestDeviceObject based on a TestStruct. Co-authored-by: Adriano Di Florio <[email protected]> Co-authored-by: Breno Orzari <[email protected]> Co-authored-by: Dimitris Papagiannis <[email protected]>
1 parent d59e80b commit 91235b5

File tree

14 files changed

+322
-5
lines changed

14 files changed

+322
-5
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#ifndef DataFormats_Portable_interface_PortableDeviceObject_h
2+
#define DataFormats_Portable_interface_PortableDeviceObject_h
3+
4+
#include <cassert>
5+
#include <optional>
6+
#include <type_traits>
7+
8+
#include <alpaka/alpaka.hpp>
9+
10+
#include "HeterogeneousCore/AlpakaInterface/interface/config.h"
11+
#include "HeterogeneousCore/AlpakaInterface/interface/memory.h"
12+
13+
// generic object in device memory
14+
template <typename T, typename TDev, typename = std::enable_if_t<alpaka::isDevice<TDev>>>
15+
class PortableDeviceObject {
16+
static_assert(not std::is_same_v<TDev, alpaka_common::DevHost>,
17+
"Use PortableHostObject<T> instead of PortableDeviceObject<T, DevHost>");
18+
19+
public:
20+
using Product = T;
21+
using Buffer = cms::alpakatools::device_buffer<TDev, Product>;
22+
using ConstBuffer = cms::alpakatools::const_device_buffer<TDev, Product>;
23+
24+
PortableDeviceObject() = default;
25+
26+
PortableDeviceObject(TDev const& device)
27+
// allocate global device memory
28+
: buffer_{cms::alpakatools::make_device_buffer<Product>(device)} {
29+
assert(reinterpret_cast<uintptr_t>(buffer_->data()) % alignof(Product) == 0);
30+
}
31+
32+
template <typename TQueue, typename = std::enable_if_t<alpaka::isQueue<TQueue>>>
33+
PortableDeviceObject(TQueue const& queue)
34+
// allocate global device memory with queue-ordered semantic
35+
: buffer_{cms::alpakatools::make_device_buffer<Product>(queue)} {
36+
assert(reinterpret_cast<uintptr_t>(buffer_->data()) % alignof(Product) == 0);
37+
}
38+
39+
// non-copyable
40+
PortableDeviceObject(PortableDeviceObject const&) = delete;
41+
PortableDeviceObject& operator=(PortableDeviceObject const&) = delete;
42+
43+
// movable
44+
PortableDeviceObject(PortableDeviceObject&&) = default;
45+
PortableDeviceObject& operator=(PortableDeviceObject&&) = default;
46+
47+
// default destructor
48+
~PortableDeviceObject() = default;
49+
50+
// access the product
51+
Product& value() { return *buffer_->data(); }
52+
Product const& value() const { return *buffer_->data(); }
53+
54+
Product* data() { return buffer_->data(); }
55+
Product const* data() const { return buffer_->data(); }
56+
57+
Product& operator*() { return *buffer_->data(); }
58+
Product const& operator*() const { return *buffer_->data(); }
59+
60+
Product* operator->() { return buffer_->data(); }
61+
Product const* operator->() const { return buffer_->data(); }
62+
63+
// access the buffer
64+
Buffer buffer() { return *buffer_; }
65+
ConstBuffer buffer() const { return *buffer_; }
66+
ConstBuffer const_buffer() const { return *buffer_; }
67+
68+
private:
69+
std::optional<Buffer> buffer_;
70+
};
71+
72+
#endif // DataFormats_Portable_interface_PortableDeviceObject_h
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#ifndef DataFormats_Portable_interface_PortableHostObject_h
2+
#define DataFormats_Portable_interface_PortableHostObject_h
3+
4+
#include <cassert>
5+
#include <optional>
6+
#include <type_traits>
7+
8+
#include <alpaka/alpaka.hpp>
9+
10+
#include "HeterogeneousCore/AlpakaInterface/interface/config.h"
11+
#include "HeterogeneousCore/AlpakaInterface/interface/host.h"
12+
#include "HeterogeneousCore/AlpakaInterface/interface/memory.h"
13+
14+
// generic object in host memory
15+
template <typename T>
16+
class PortableHostObject {
17+
public:
18+
using Product = T;
19+
using Buffer = cms::alpakatools::host_buffer<Product>;
20+
using ConstBuffer = cms::alpakatools::const_host_buffer<Product>;
21+
22+
PortableHostObject() = default;
23+
24+
PortableHostObject(alpaka_common::DevHost const& host)
25+
// allocate pageable host memory
26+
: buffer_{cms::alpakatools::make_host_buffer<Product>()}, product_{buffer_->data()} {
27+
assert(reinterpret_cast<uintptr_t>(product_) % alignof(Product) == 0);
28+
}
29+
30+
template <typename TQueue, typename = std::enable_if_t<alpaka::isQueue<TQueue>>>
31+
PortableHostObject(TQueue const& queue)
32+
// allocate pinned host memory associated to the given work queue, accessible by the queue's device
33+
: buffer_{cms::alpakatools::make_host_buffer<Product>(queue)}, product_{buffer_->data()} {
34+
assert(reinterpret_cast<uintptr_t>(product_) % alignof(Product) == 0);
35+
}
36+
37+
// non-copyable
38+
PortableHostObject(PortableHostObject const&) = delete;
39+
PortableHostObject& operator=(PortableHostObject const&) = delete;
40+
41+
// movable
42+
PortableHostObject(PortableHostObject&&) = default;
43+
PortableHostObject& operator=(PortableHostObject&&) = default;
44+
45+
// default destructor
46+
~PortableHostObject() = default;
47+
48+
// access the product
49+
Product& value() { return *product_; }
50+
Product const& value() const { return *product_; }
51+
52+
Product* data() { return product_; }
53+
Product const* data() const { return product_; }
54+
55+
Product& operator*() { return *product_; }
56+
Product const& operator*() const { return *product_; }
57+
58+
Product* operator->() { return product_; }
59+
Product const* operator->() const { return product_; }
60+
61+
// access the buffer
62+
Buffer buffer() { return *buffer_; }
63+
ConstBuffer buffer() const { return *buffer_; }
64+
ConstBuffer const_buffer() const { return *buffer_; }
65+
66+
// part of the ROOT read streamer
67+
static void ROOTReadStreamer(PortableHostObject* newObj, Product& product) {
68+
// destroy the default-constructed object
69+
newObj->~PortableHostObject();
70+
// use the global "host" object returned by cms::alpakatools::host()
71+
new (newObj) PortableHostObject(cms::alpakatools::host());
72+
// copy the data from the on-file object to the new one
73+
std::memcpy(newObj->product_, &product, sizeof(Product));
74+
}
75+
76+
private:
77+
std::optional<Buffer> buffer_; //!
78+
Product* product_;
79+
};
80+
81+
#endif // DataFormats_Portable_interface_PortableHostObject_h
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#ifndef DataFormats_Portable_interface_PortableObject_h
2+
#define DataFormats_Portable_interface_PortableObject_h
3+
4+
#include <type_traits>
5+
6+
#include "HeterogeneousCore/AlpakaInterface/interface/traits.h"
7+
8+
namespace traits {
9+
10+
// trait for a generic SoA-based product
11+
template <typename T, typename TDev, typename = std::enable_if_t<alpaka::isDevice<TDev>>>
12+
class PortableObjectTrait;
13+
14+
} // namespace traits
15+
16+
// type alias for a generic SoA-based product
17+
template <typename T, typename TDev, typename = std::enable_if_t<alpaka::isDevice<TDev>>>
18+
using PortableObject = typename traits::PortableObjectTrait<T, TDev>::ProductType;
19+
20+
#endif // DataFormats_Portable_interface_PortableObject_h

DataFormats/Portable/interface/alpaka/PortableCollection.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
#ifndef DataFormats_Portable_interface_alpaka_PortableDeviceCollection_h
2-
#define DataFormats_Portable_interface_alpaka_PortableDeviceCollection_h
3-
4-
#include <optional>
1+
#ifndef DataFormats_Portable_interface_alpaka_PortableCollection_h
2+
#define DataFormats_Portable_interface_alpaka_PortableCollection_h
53

64
#include <alpaka/alpaka.hpp>
75

@@ -68,4 +66,4 @@ namespace cms::alpakatools {
6866
};
6967
} // namespace cms::alpakatools
7068

71-
#endif // DataFormats_Portable_interface_alpaka_PortableDeviceCollection_h
69+
#endif // DataFormats_Portable_interface_alpaka_PortableCollection_h
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#ifndef DataFormats_Portable_interface_alpaka_PortableObject_h
2+
#define DataFormats_Portable_interface_alpaka_PortableObject_h
3+
4+
#include <alpaka/alpaka.hpp>
5+
6+
#include "DataFormats/Portable/interface/PortableObject.h"
7+
#include "DataFormats/Portable/interface/PortableHostObject.h"
8+
#include "DataFormats/Portable/interface/PortableDeviceObject.h"
9+
#include "HeterogeneousCore/AlpakaInterface/interface/config.h"
10+
#include "HeterogeneousCore/AlpakaInterface/interface/CopyToDevice.h"
11+
#include "HeterogeneousCore/AlpakaInterface/interface/CopyToHost.h"
12+
13+
// This header is not used by PortableObject, but is included here to automatically
14+
// provide its content to users of ALPAKA_ACCELERATOR_NAMESPACE::PortableObject.
15+
#include "HeterogeneousCore/AlpakaInterface/interface/AssertDeviceMatchesHostCollection.h"
16+
17+
namespace ALPAKA_ACCELERATOR_NAMESPACE {
18+
19+
#if defined ALPAKA_ACC_CPU_B_SEQ_T_SEQ_ENABLED
20+
// ... or any other CPU-based accelerators
21+
22+
// generic SoA-based product in host memory
23+
template <typename T>
24+
using PortableObject = ::PortableHostObject<T>;
25+
26+
#else
27+
28+
// generic SoA-based product in device memory
29+
template <typename T>
30+
using PortableObject = ::PortableDeviceObject<T, Device>;
31+
32+
#endif // ALPAKA_ACC_CPU_B_SEQ_T_SEQ_ENABLED
33+
34+
} // namespace ALPAKA_ACCELERATOR_NAMESPACE
35+
36+
namespace traits {
37+
38+
// specialise the trait for the device provided by the ALPAKA_ACCELERATOR_NAMESPACE
39+
template <typename T>
40+
class PortableObjectTrait<T, ALPAKA_ACCELERATOR_NAMESPACE::Device> {
41+
using ProductType = ALPAKA_ACCELERATOR_NAMESPACE::PortableObject<T>;
42+
};
43+
44+
} // namespace traits
45+
46+
namespace cms::alpakatools {
47+
template <typename TProduct, typename TDevice>
48+
struct CopyToHost<PortableDeviceObject<TProduct, TDevice>> {
49+
template <typename TQueue>
50+
static auto copyAsync(TQueue& queue, PortableDeviceObject<TProduct, TDevice> const& srcData) {
51+
PortableHostObject<TProduct> dstData(queue);
52+
alpaka::memcpy(queue, dstData.buffer(), srcData.buffer());
53+
return dstData;
54+
}
55+
};
56+
57+
template <typename TProduct>
58+
struct CopyToDevice<PortableHostObject<TProduct>> {
59+
template <typename TQueue>
60+
static auto copyAsync(TQueue& queue, PortableHostObject<TProduct> const& srcData) {
61+
using TDevice = typename alpaka::trait::DevType<TQueue>::type;
62+
PortableDeviceObject<TProduct, TDevice> dstData(queue);
63+
alpaka::memcpy(queue, dstData.buffer(), srcData.buffer());
64+
return dstData;
65+
}
66+
};
67+
} // namespace cms::alpakatools
68+
69+
#endif // DataFormats_Portable_interface_alpaka_PortableObject_h
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#ifndef DataFormats_PortableTestObjects_interface_TestHostObject_h
2+
#define DataFormats_PortableTestObjects_interface_TestHostObject_h
3+
4+
#include "DataFormats/Portable/interface/PortableHostObject.h"
5+
#include "DataFormats/PortableTestObjects/interface/TestStruct.h"
6+
7+
namespace portabletest {
8+
9+
// struct with x, y, z, id fields in host memory
10+
using TestHostObject = PortableHostObject<TestStruct>;
11+
12+
} // namespace portabletest
13+
14+
#endif // DataFormats_PortableTestObjects_interface_TestHostObject_h
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#ifndef DataFormats_PortableTestObjects_interface_TestStruct_h
2+
#define DataFormats_PortableTestObjects_interface_TestStruct_h
3+
4+
#include <cstdint>
5+
6+
namespace portabletest {
7+
8+
// struct with x, y, z, id fields
9+
struct TestStruct {
10+
double x;
11+
double y;
12+
double z;
13+
int32_t id;
14+
};
15+
16+
} // namespace portabletest
17+
18+
#endif // DataFormats_PortableTestObjects_interface_TestStruct_h
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#ifndef DataFormats_PortableTestObjects_interface_alpaka_TestDeviceObject_h
2+
#define DataFormats_PortableTestObjects_interface_alpaka_TestDeviceObject_h
3+
4+
#include "DataFormats/Portable/interface/alpaka/PortableObject.h"
5+
#include "DataFormats/PortableTestObjects/interface/TestHostObject.h"
6+
#include "DataFormats/PortableTestObjects/interface/TestStruct.h"
7+
#include "HeterogeneousCore/AlpakaInterface/interface/config.h"
8+
9+
namespace ALPAKA_ACCELERATOR_NAMESPACE {
10+
11+
namespace portabletest {
12+
13+
// make the names from the top-level portabletest namespace visible for unqualified lookup
14+
// inside the ALPAKA_ACCELERATOR_NAMESPACE::portabletest namespace
15+
using namespace ::portabletest;
16+
17+
// struct with x, y, z, id fields in device global memory
18+
using TestDeviceObject = PortableObject<TestStruct>;
19+
20+
} // namespace portabletest
21+
22+
} // namespace ALPAKA_ACCELERATOR_NAMESPACE
23+
24+
// check that the portable device collection for the host device is the same as the portable host collection
25+
ASSERT_DEVICE_MATCHES_HOST_COLLECTION(portabletest::TestDeviceObject, portabletest::TestHostObject);
26+
27+
#endif // DataFormats_PortableTestObjects_interface_alpaka_TestDeviceObject_h
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#include "DataFormats/Common/interface/DeviceProduct.h"
22
#include "DataFormats/Common/interface/Wrapper.h"
33
#include "DataFormats/PortableTestObjects/interface/TestSoA.h"
4+
#include "DataFormats/PortableTestObjects/interface/TestStruct.h"
45
#include "DataFormats/PortableTestObjects/interface/alpaka/TestDeviceCollection.h"
6+
#include "DataFormats/PortableTestObjects/interface/alpaka/TestDeviceObject.h"

DataFormats/PortableTestObjects/src/alpaka/classes_cuda_def.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,8 @@
22
<class name="alpaka_cuda_async::portabletest::TestDeviceCollection" persistent="false"/>
33
<class name="edm::DeviceProduct<alpaka_cuda_async::portabletest::TestDeviceCollection>" persistent="false"/>
44
<class name="edm::Wrapper<edm::DeviceProduct<alpaka_cuda_async::portabletest::TestDeviceCollection>>" persistent="false"/>
5+
6+
<class name="alpaka_cuda_async::portabletest::TestDeviceObject" persistent="false"/>
7+
<class name="edm::DeviceProduct<alpaka_cuda_async::portabletest::TestDeviceObject>" persistent="false"/>
8+
<class name="edm::Wrapper<edm::DeviceProduct<alpaka_cuda_async::portabletest::TestDeviceObject>>" persistent="false"/>
59
</lcgdict>

0 commit comments

Comments
 (0)