Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2025-06-26 23:26:38

0001 #ifndef DataFormats_Portable_interface_PortableHostCollection_h
0002 #define DataFormats_Portable_interface_PortableHostCollection_h
0003 
0004 #include <cassert>
0005 #include <optional>
0006 
0007 #include <alpaka/alpaka.hpp>
0008 
0009 #include "DataFormats/Common/interface/Uninitialized.h"
0010 #include "DataFormats/Portable/interface/PortableCollectionCommon.h"
0011 #include "HeterogeneousCore/AlpakaInterface/interface/config.h"
0012 #include "HeterogeneousCore/AlpakaInterface/interface/host.h"
0013 #include "HeterogeneousCore/AlpakaInterface/interface/memory.h"
0014 
0015 // generic SoA-based product in host memory
0016 template <typename T>
0017 class PortableHostCollection {
0018 public:
0019   using Layout = T;
0020   using View = typename Layout::View;
0021   using ConstView = typename Layout::ConstView;
0022   using Descriptor = typename Layout::Descriptor;
0023   using ConstDescriptor = typename Layout::ConstDescriptor;
0024   using Buffer = cms::alpakatools::host_buffer<std::byte[]>;
0025   using ConstBuffer = cms::alpakatools::const_host_buffer<std::byte[]>;
0026 
0027   PortableHostCollection() = delete;
0028 
0029   explicit PortableHostCollection(edm::Uninitialized) noexcept {};
0030 
0031   PortableHostCollection(int32_t elements, alpaka_common::DevHost const& host)
0032       // allocate pageable host memory
0033       : buffer_{cms::alpakatools::make_host_buffer<std::byte[]>(Layout::computeDataSize(elements))},
0034         layout_{buffer_->data(), elements},
0035         view_{layout_} {
0036     // Alpaka set to a default alignment of 128 bytes defining ALPAKA_DEFAULT_HOST_MEMORY_ALIGNMENT=128
0037     assert(reinterpret_cast<uintptr_t>(buffer_->data()) % Layout::alignment == 0);
0038   }
0039 
0040   template <typename TQueue, typename = std::enable_if_t<alpaka::isQueue<TQueue>>>
0041   PortableHostCollection(int32_t elements, TQueue const& queue)
0042       // allocate pinned host memory associated to the given work queue, accessible by the queue's device
0043       : buffer_{cms::alpakatools::make_host_buffer<std::byte[]>(queue, Layout::computeDataSize(elements))},
0044         layout_{buffer_->data(), elements},
0045         view_{layout_} {
0046     // Alpaka set to a default alignment of 128 bytes defining ALPAKA_DEFAULT_HOST_MEMORY_ALIGNMENT=128
0047     assert(reinterpret_cast<uintptr_t>(buffer_->data()) % Layout::alignment == 0);
0048   }
0049 
0050   // non-copyable
0051   PortableHostCollection(PortableHostCollection const&) = delete;
0052   PortableHostCollection& operator=(PortableHostCollection const&) = delete;
0053 
0054   // movable
0055   PortableHostCollection(PortableHostCollection&&) = default;
0056   PortableHostCollection& operator=(PortableHostCollection&&) = default;
0057 
0058   // default destructor
0059   ~PortableHostCollection() = default;
0060 
0061   // access the View
0062   View& view() { return view_; }
0063   ConstView const& view() const { return view_; }
0064   ConstView const& const_view() const { return view_; }
0065 
0066   View& operator*() { return view_; }
0067   ConstView const& operator*() const { return view_; }
0068 
0069   View* operator->() { return &view_; }
0070   ConstView const* operator->() const { return &view_; }
0071 
0072   // access the Buffer
0073   Buffer buffer() { return *buffer_; }
0074   ConstBuffer buffer() const { return *buffer_; }
0075   ConstBuffer const_buffer() const { return *buffer_; }
0076 
0077   // erases the data in the Buffer by writing zeros (bytes containing '\0') to it
0078   void zeroInitialise() {
0079     std::memset(std::data(*buffer_), 0x00, alpaka::getExtentProduct(*buffer_) * sizeof(std::byte));
0080   }
0081 
0082   template <typename TQueue, typename = std::enable_if_t<alpaka::isQueue<TQueue>>>
0083   void zeroInitialise(TQueue&& queue) {
0084     alpaka::memset(std::forward<TQueue>(queue), *buffer_, 0x00);
0085   }
0086 
0087   // part of the ROOT read streamer
0088   static void ROOTReadStreamer(PortableHostCollection* newObj, Layout& layout) {
0089     // destroy the default-constructed collection
0090     newObj->~PortableHostCollection();
0091     // construct in-place a new collection, with the known size, using the global "host" object returned by cms::alpakatools::host()
0092     new (newObj) PortableHostCollection(layout.metadata().size(), cms::alpakatools::host());
0093     // copy the data from the on-file layout to the new collection
0094     newObj->layout_.ROOTReadStreamer(layout);
0095     // free the memory allocated by ROOT
0096     layout.ROOTStreamerCleaner();
0097   }
0098 
0099   // Copy column by column the content of the given view into this PortableHostCollection.
0100   // The view must point to data in host memory.
0101   void deepCopy(ConstView const& view) { layout_.deepCopy(view); }
0102 
0103   // Copy column by column heterogeneously for device to host data transfer.
0104   template <typename TQueue>
0105   void deepCopy(ConstView const& view, TQueue& queue) {
0106     ConstDescriptor desc{view};
0107     Descriptor desc_{view_};
0108     _deepCopy<0>(desc_, desc, queue);
0109   }
0110 
0111 private:
0112   // Helper function implementing the recursive deep copy
0113   template <int I, typename TQueue>
0114   void _deepCopy(Descriptor& dest, ConstDescriptor const& src, TQueue& queue) {
0115     if constexpr (I < ConstDescriptor::num_cols) {
0116       assert(std::get<I>(dest.buff).size_bytes() == std::get<I>(src.buff).size_bytes());
0117       alpaka::memcpy(
0118           queue,
0119           alpaka::createView(alpaka::getDev(queue), std::get<I>(dest.buff).data(), std::get<I>(dest.buff).size()),
0120           alpaka::createView(alpaka::getDev(queue), std::get<I>(src.buff).data(), std::get<I>(src.buff).size()));
0121       _deepCopy<I + 1>(dest, src, queue);
0122     }
0123   }
0124 
0125   // Data members
0126   std::optional<Buffer> buffer_;  //!
0127   Layout layout_;                 //
0128   View view_;                     //!
0129 };
0130 
0131 // generic SoA-based product in host memory
0132 template <typename T0, typename... Args>
0133 class PortableHostMultiCollection {
0134   template <typename T>
0135   static constexpr std::size_t count_t_ = portablecollection::typeCount<T, T0, Args...>;
0136 
0137   template <typename T>
0138   static constexpr std::size_t index_t_ = portablecollection::typeIndex<T, T0, Args...>;
0139 
0140   static constexpr std::size_t members_ = portablecollection::membersCount<T0, Args...>;
0141 
0142 public:
0143   using Buffer = cms::alpakatools::host_buffer<std::byte[]>;
0144   using ConstBuffer = cms::alpakatools::const_host_buffer<std::byte[]>;
0145   using Implementation = portablecollection::CollectionImpl<0, T0, Args...>;
0146 
0147   using SizesArray = std::array<int32_t, members_>;
0148 
0149   template <std::size_t Idx = 0, typename = std::enable_if_t<(members_ > Idx)>>
0150   using Layout = portablecollection::TypeResolver<Idx, T0, Args...>;
0151   template <std::size_t Idx = 0, typename = std::enable_if_t<(members_ > Idx)>>
0152   using View = typename Layout<Idx>::View;
0153   template <std::size_t Idx = 0, typename = std::enable_if_t<(members_ > Idx)>>
0154   using ConstView = typename Layout<Idx>::ConstView;
0155 
0156 private:
0157   template <std::size_t Idx>
0158   using Leaf = portablecollection::CollectionLeaf<Idx, Layout<Idx>>;
0159 
0160   template <std::size_t Idx>
0161   Leaf<Idx>& get() {
0162     return static_cast<Leaf<Idx>&>(impl_);
0163   }
0164 
0165   template <std::size_t Idx>
0166   Leaf<Idx> const& get() const {
0167     return static_cast<Leaf<Idx> const&>(impl_);
0168   }
0169 
0170   template <typename T>
0171   portablecollection::CollectionLeaf<index_t_<T>, T>& get() {
0172     return static_cast<portablecollection::CollectionLeaf<index_t_<T>, T>&>(impl_);
0173   }
0174 
0175   template <typename T>
0176   const portablecollection::CollectionLeaf<index_t_<T>, T>& get() const {
0177     return static_cast<const portablecollection::CollectionLeaf<index_t_<T>, T>&>(impl_);
0178   }
0179 
0180   static int32_t computeDataSize(const std::array<int32_t, members_>& sizes) {
0181     int32_t ret = 0;
0182     portablecollection::constexpr_for<0, members_>(
0183         [&sizes, &ret](auto i) { ret += Layout<i>::computeDataSize(sizes[i]); });
0184     return ret;
0185   }
0186 
0187 public:
0188   PortableHostMultiCollection() = delete;
0189 
0190   explicit PortableHostMultiCollection(edm::Uninitialized) noexcept {};
0191 
0192   PortableHostMultiCollection(int32_t elements, alpaka_common::DevHost const& host)
0193       // allocate pageable host memory
0194       : buffer_{cms::alpakatools::make_host_buffer<std::byte[]>(Layout<>::computeDataSize(elements))},
0195         impl_{buffer_->data(), elements} {
0196     // Alpaka set to a default alignment of 128 bytes defining ALPAKA_DEFAULT_HOST_MEMORY_ALIGNMENT=128
0197     assert(reinterpret_cast<uintptr_t>(buffer_->data()) % Layout<>::alignment == 0);
0198     static_assert(members_ == 1);
0199   }
0200 
0201   template <typename TQueue, typename = std::enable_if_t<alpaka::isQueue<TQueue>>>
0202   PortableHostMultiCollection(int32_t elements, TQueue const& queue)
0203       // allocate pinned host memory associated to the given work queue, accessible by the queue's device
0204       : buffer_{cms::alpakatools::make_host_buffer<std::byte[]>(queue, Layout<>::computeDataSize(elements))},
0205         impl_{buffer_->data(), elements} {
0206     // Alpaka set to a default alignment of 128 bytes defining ALPAKA_DEFAULT_HOST_MEMORY_ALIGNMENT=128
0207     assert(reinterpret_cast<uintptr_t>(buffer_->data()) % Layout<>::alignment == 0);
0208     static_assert(members_ == 1);
0209   }
0210 
0211   PortableHostMultiCollection(const std::array<int32_t, members_>& sizes, alpaka_common::DevHost const& host)
0212       // allocate pinned host memory associated to the given work queue, accessible by the queue's device
0213       : buffer_{cms::alpakatools::make_host_buffer<std::byte[]>(computeDataSize(sizes))},
0214         impl_{buffer_->data(), sizes} {
0215     // Alpaka set to a default alignment of 128 bytes defining ALPAKA_DEFAULT_HOST_MEMORY_ALIGNMENT=128
0216     portablecollection::constexpr_for<0, members_>(
0217         [&](auto i) { assert(reinterpret_cast<uintptr_t>(buffer_->data()) % Layout<i>::alignment == 0); });
0218     constexpr auto alignment = Layout<0>::alignment;
0219     portablecollection::constexpr_for<1, members_>(
0220         [&alignment](auto i) { static_assert(alignment == Layout<i>::alignment); });
0221   }
0222 
0223   template <typename TQueue, typename = std::enable_if_t<alpaka::isQueue<TQueue>>>
0224   PortableHostMultiCollection(const std::array<int32_t, members_>& sizes, TQueue const& queue)
0225       // allocate pinned host memory associated to the given work queue, accessible by the queue's device
0226       : buffer_{cms::alpakatools::make_host_buffer<std::byte[]>(queue, computeDataSize(sizes))},
0227         impl_{buffer_->data(), sizes} {
0228     // Alpaka set to a default alignment of 128 bytes defining ALPAKA_DEFAULT_HOST_MEMORY_ALIGNMENT=128
0229     portablecollection::constexpr_for<0, members_>(
0230         [&](auto i) { assert(reinterpret_cast<uintptr_t>(buffer_->data()) % Layout<i>::alignment == 0); });
0231     constexpr auto alignment = Layout<0>::alignment;
0232     portablecollection::constexpr_for<1, members_>(
0233         [&alignment](auto i) { static_assert(alignment == Layout<i>::alignment); });
0234   }
0235 
0236   // non-copyable
0237   PortableHostMultiCollection(PortableHostMultiCollection const&) = delete;
0238   PortableHostMultiCollection& operator=(PortableHostMultiCollection const&) = delete;
0239 
0240   // movable
0241   PortableHostMultiCollection(PortableHostMultiCollection&&) = default;
0242   PortableHostMultiCollection& operator=(PortableHostMultiCollection&&) = default;
0243 
0244   // default destructor
0245   ~PortableHostMultiCollection() = default;
0246 
0247   // access the View by index
0248   template <std::size_t Idx = 0, typename = std::enable_if_t<(members_ > Idx)>>
0249   View<Idx>& view() {
0250     return get<Idx>().view_;
0251   }
0252 
0253   template <std::size_t Idx = 0, typename = std::enable_if_t<(members_ > Idx)>>
0254   ConstView<Idx> const& view() const {
0255     return get<Idx>().view_;
0256   }
0257 
0258   template <std::size_t Idx = 0, typename = std::enable_if_t<(members_ > Idx)>>
0259   ConstView<Idx> const& const_view() const {
0260     return get<Idx>().view_;
0261   }
0262 
0263   template <std::size_t Idx = 0, typename = std::enable_if_t<(members_ > Idx)>>
0264   View<Idx>& operator*() {
0265     return get<Idx>().view_;
0266   }
0267 
0268   template <std::size_t Idx = 0, typename = std::enable_if_t<(members_ > Idx)>>
0269   ConstView<Idx> const& operator*() const {
0270     return get<Idx>().view_;
0271   }
0272 
0273   template <std::size_t Idx = 0, typename = std::enable_if_t<(members_ > Idx)>>
0274   View<Idx>* operator->() {
0275     return &get<Idx>().view_;
0276   }
0277 
0278   template <std::size_t Idx = 0, typename = std::enable_if_t<(members_ > Idx)>>
0279   ConstView<Idx> const* operator->() const {
0280     return &get<Idx>().view_;
0281   }
0282 
0283   // access the View by type
0284   template <typename T>
0285   typename T::View& view() {
0286     return get<T>().view_;
0287   }
0288 
0289   template <typename T>
0290   typename T::ConstView const& view() const {
0291     return get<T>().view_;
0292   }
0293 
0294   template <typename T>
0295   typename T::ConstView const& const_view() const {
0296     return get<T>().view_;
0297   }
0298 
0299   template <typename T>
0300   typename T::View& operator*() {
0301     return get<T>().view_;
0302   }
0303 
0304   template <typename T>
0305   typename T::ConstView const& operator*() const {
0306     return get<T>().view_;
0307   }
0308 
0309   template <typename T>
0310   typename T::View* operator->() {
0311     return &get<T>().view_;
0312   }
0313 
0314   template <typename T>
0315   typename T::ConstView const* operator->() const {
0316     return &get<T>().view_;
0317   }
0318 
0319   // access the Buffer
0320   Buffer buffer() { return *buffer_; }
0321   ConstBuffer buffer() const { return *buffer_; }
0322   ConstBuffer const_buffer() const { return *buffer_; }
0323 
0324   // erases the data in the Buffer by writing zeros (bytes containing '\0') to it
0325   void zeroInitialise() {
0326     std::memset(std::data(*buffer_), 0x00, alpaka::getExtentProduct(*buffer_) * sizeof(std::byte));
0327   }
0328 
0329   template <typename TQueue, typename = std::enable_if_t<alpaka::isQueue<TQueue>>>
0330   void zeroInitialise(TQueue&& queue) {
0331     alpaka::memset(std::forward<TQueue>(queue), *buffer_, 0x00);
0332   }
0333 
0334   // extract the sizes array
0335   SizesArray sizes() const {
0336     SizesArray ret;
0337     portablecollection::constexpr_for<0, members_>([&](auto i) { ret[i] = get<i>().layout_.metadata().size(); });
0338     return ret;
0339   }
0340 
0341   // part of the ROOT read streamer
0342   static void ROOTReadStreamer(PortableHostMultiCollection* newObj, Implementation& onfileImpl) {
0343     newObj->~PortableHostMultiCollection();
0344     // use the global "host" object returned by cms::alpakatools::host()
0345     std::array<int32_t, members_> sizes;
0346     portablecollection::constexpr_for<0, members_>([&sizes, &onfileImpl](auto i) {
0347       sizes[i] = static_cast<Leaf<i> const&>(onfileImpl).layout_.metadata().size();
0348     });
0349     new (newObj) PortableHostMultiCollection(sizes, cms::alpakatools::host());
0350     portablecollection::constexpr_for<0, members_>([&newObj, &onfileImpl](auto i) {
0351       static_cast<Leaf<i>&>(newObj->impl_).layout_.ROOTReadStreamer(static_cast<Leaf<i> const&>(onfileImpl).layout_);
0352       static_cast<Leaf<i>&>(onfileImpl).layout_.ROOTStreamerCleaner();
0353     });
0354   }
0355 
0356 private:
0357   std::optional<Buffer> buffer_;  //!
0358   Implementation impl_;           // (serialized: this is where the layouts live)
0359 };
0360 
0361 // Singleton case does not need to be aliased. A special template covers it.
0362 
0363 // This aliasing is needed to work with ROOT serialization. Bare templates make dictionary compilation fail.
0364 template <typename T0, typename T1>
0365 using PortableHostCollection2 = ::PortableHostMultiCollection<T0, T1>;
0366 
0367 template <typename T0, typename T1, typename T2>
0368 using PortableHostCollection3 = ::PortableHostMultiCollection<T0, T1, T2>;
0369 
0370 template <typename T0, typename T1, typename T2, typename T3>
0371 using PortableHostCollection4 = ::PortableHostMultiCollection<T0, T1, T2, T3>;
0372 
0373 template <typename T0, typename T1, typename T2, typename T3, typename T4>
0374 using PortableHostCollection5 = ::PortableHostMultiCollection<T0, T1, T2, T3, T4>;
0375 
0376 #endif  // DataFormats_Portable_interface_PortableHostCollection_h