PortableHostObject

Macros

Line Code
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
#ifndef DataFormats_Portable_interface_PortableHostObject_h
#define DataFormats_Portable_interface_PortableHostObject_h

#include <cassert>
#include <optional>
#include <type_traits>

#include <alpaka/alpaka.hpp>

#include "DataFormats/Common/interface/Uninitialized.h"
#include "HeterogeneousCore/AlpakaInterface/interface/config.h"
#include "HeterogeneousCore/AlpakaInterface/interface/host.h"
#include "HeterogeneousCore/AlpakaInterface/interface/memory.h"

// generic object in host memory
template <typename T>
class PortableHostObject {
public:
  using Product = T;
  using Buffer = cms::alpakatools::host_buffer<Product>;
  using ConstBuffer = cms::alpakatools::const_host_buffer<Product>;

  static_assert(std::is_trivially_destructible_v<Product>);

  PortableHostObject() = delete;

  PortableHostObject(edm::Uninitialized) noexcept {}

  // Note that in contrast to the variadic template overload, this
  // constructor does not initialize the contained object
  PortableHostObject(alpaka_common::DevHost const& host)
      // allocate pageable host memory
      : buffer_{cms::alpakatools::make_host_buffer<Product>()}, product_{buffer_->data()} {
    assert(reinterpret_cast<uintptr_t>(product_) % alignof(Product) == 0);
  }

  template <typename... Args>
  PortableHostObject(alpaka_common::DevHost const& host, Args&&... args)
      // allocate pageable host memory
      : buffer_{cms::alpakatools::make_host_buffer<Product>()},
        product_{new(buffer_->data()) Product(std::forward<Args>(args)...)} {
    assert(reinterpret_cast<uintptr_t>(product_) % alignof(Product) == 0);
  }

  // Note that in contrast to the variadic template overload, this
  // constructor does not initialize the contained object
  template <typename TQueue, typename = std::enable_if_t<alpaka::isQueue<TQueue>>>
  PortableHostObject(TQueue const& queue)
      // allocate pinned host memory associated to the given work queue, accessible by the queue's device
      : buffer_{cms::alpakatools::make_host_buffer<Product>(queue)}, product_{buffer_->data()} {
    assert(reinterpret_cast<uintptr_t>(product_) % alignof(Product) == 0);
  }

  template <typename TQueue, typename... Args, typename = std::enable_if_t<alpaka::isQueue<TQueue>>>
  PortableHostObject(TQueue const& queue, Args&&... args)
      // allocate pinned host memory associated to the given work queue, accessible by the queue's device
      : buffer_{cms::alpakatools::make_host_buffer<Product>(queue)},
        product_{new(buffer_->data()) Product(std::forward<Args>(args)...)} {
    assert(reinterpret_cast<uintptr_t>(product_) % alignof(Product) == 0);
  }

  // non-copyable
  PortableHostObject(PortableHostObject const&) = delete;
  PortableHostObject& operator=(PortableHostObject const&) = delete;

  // movable
  PortableHostObject(PortableHostObject&&) = default;
  PortableHostObject& operator=(PortableHostObject&&) = default;

  // default destructor
  ~PortableHostObject() = default;

  // access the product
  Product& value() { return *product_; }
  Product const& value() const { return *product_; }
  Product const& const_value() const { return *product_; }

  Product* data() { return product_; }
  Product const* data() const { return product_; }
  Product const* const_data() const { return product_; }

  Product& operator*() { return *product_; }
  Product const& operator*() const { return *product_; }

  Product* operator->() { return product_; }
  Product const* operator->() const { return product_; }

  // access the buffer
  Buffer buffer() { return *buffer_; }
  ConstBuffer buffer() const { return *buffer_; }
  ConstBuffer const_buffer() const { return *buffer_; }

  // erases the data in the Buffer by writing zeros (bytes containing '\0') to it
  void zeroInitialise() {
    std::memset(std::data(*buffer_), 0x00, alpaka::getExtentProduct(*buffer_) * sizeof(std::byte));
  }

  template <typename TQueue, typename = std::enable_if_t<alpaka::isQueue<TQueue>>>
  void zeroInitialise(TQueue&& queue) {
    alpaka::memset(std::forward<TQueue>(queue), *buffer_, 0x00);
  }

  // part of the ROOT read streamer
  static void ROOTReadStreamer(PortableHostObject* newObj, Product& product) {
    // destroy the default-constructed object
    newObj->~PortableHostObject();
    // use the global "host" object returned by cms::alpakatools::host()
    new (newObj) PortableHostObject(cms::alpakatools::host());
    // copy the data from the on-file object to the new one
    std::memcpy(newObj->product_, &product, sizeof(Product));
  }

private:
  std::optional<Buffer> buffer_;  //!
  Product* product_;
};

#endif  // DataFormats_Portable_interface_PortableHostObject_h