Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2025-04-06 22:42:47

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