Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-09-08 23:51:35

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