Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:54:47

0001 //----------------------------------*-C++-*----------------------------------//
0002 // Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers.
0003 // See the top-level COPYRIGHT file for details.
0004 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0005 //---------------------------------------------------------------------------//
0006 //! \file corecel/data/DeviceVector.hh
0007 //---------------------------------------------------------------------------//
0008 #pragma once
0009 
0010 #include <type_traits>
0011 
0012 #include "corecel/cont/InitializedValue.hh"
0013 #include "corecel/cont/Span.hh"
0014 #include "corecel/sys/ThreadId.hh"
0015 
0016 #include "DeviceAllocation.hh"
0017 #include "ObserverPtr.hh"
0018 
0019 namespace celeritas
0020 {
0021 //---------------------------------------------------------------------------//
0022 /*!
0023  * Host vector for managing uninitialized device-storage data.
0024  *
0025  * This is a class used only in host memory (not passed to kernels) to manage
0026  * device allocation and host/device copies.  It does \em not perform
0027  * initialization on the data: the host code must define and copy over suitable
0028  * data.
0029  *
0030  * For more complex data usage (dynamic size increases, std vector-like access,
0031  * object initialization), use \c thrust::device_vector inside a \c .cu file.
0032  *
0033  * When a \c StreamId is passed as the last constructor argument,
0034  * all memory operations are asynchronous and ordered within that stream.
0035  *
0036  * \code
0037     DeviceVector<double> myvec(100);
0038     myvec.copy_to_device(make_span(hostvec));
0039     myvec.copy_to_host(make_span(hostvec));
0040    \endcode
0041  *
0042  * - TODO: remove stream?
0043  * - TODO: move to detail since this is basically only a backend for Collection
0044  */
0045 template<class T>
0046 class DeviceVector
0047 {
0048 #if !CELERITAS_USE_HIP
0049     // rocrand states have nontrivial destructors, and some HIP integer types
0050     // are not trivially copyable
0051     static_assert(std::is_trivially_copyable<T>::value,
0052                   "DeviceVector element is not trivially copyable");
0053 
0054     static_assert(std::is_trivially_destructible<T>::value,
0055                   "DeviceVector element is not trivially destructible");
0056 #endif
0057 
0058   public:
0059     //!@{
0060     //! \name Type aliases
0061     using value_type = T;
0062     using SpanT = Span<T>;
0063     using SpanConstT = Span<T const>;
0064     //!@}
0065 
0066   public:
0067     // Construct with no elements
0068     DeviceVector() = default;
0069 
0070     // Construct with no elements
0071     explicit DeviceVector(StreamId stream);
0072 
0073     // Construct with a number of elements
0074     explicit DeviceVector(size_type count);
0075 
0076     // Construct with a number of elements
0077     DeviceVector(size_type count, StreamId stream);
0078 
0079     // Swap with another vector
0080     inline void swap(DeviceVector& other) noexcept;
0081 
0082     // Allocate and copy from host pointers
0083     void assign(T const* first, T const* last);
0084 
0085     //// ACCESSORS ////
0086 
0087     //! Get the number of elements
0088     size_type size() const { return size_; }
0089 
0090     //! Whether any elements are stored
0091     bool empty() const { return size_ == 0; }
0092 
0093     //// DEVICE ACCESSORS ////
0094 
0095     // Copy data to device
0096     inline void copy_to_device(SpanConstT host_data);
0097 
0098     // Copy data to host
0099     inline void copy_to_host(SpanT host_data) const;
0100 
0101     // Get a mutable view to device data
0102     SpanT device_ref() { return {this->data(), this->size()}; }
0103 
0104     // Get a const view to device data
0105     SpanConstT device_ref() const { return {this->data(), this->size()}; }
0106 
0107     // Raw pointer to device data (dangerous!)
0108     inline T* data();
0109 
0110     // Raw pointer to device data (dangerous!)
0111     inline T const* data() const;
0112 
0113   private:
0114     DeviceAllocation allocation_;
0115     InitializedValue<size_type> size_;
0116 };
0117 
0118 // Swap two vectors.
0119 template<class T>
0120 inline void swap(DeviceVector<T>& a, DeviceVector<T>& b) noexcept;
0121 
0122 //---------------------------------------------------------------------------//
0123 // INLINE DEFINITIONS
0124 //---------------------------------------------------------------------------//
0125 /*!
0126  * Construct with a stream.
0127  */
0128 template<class T>
0129 DeviceVector<T>::DeviceVector(StreamId stream) : allocation_{stream}, size_{0}
0130 {
0131 }
0132 
0133 //---------------------------------------------------------------------------//
0134 /*!
0135  * Construct with a number of allocated elements.
0136  */
0137 template<class T>
0138 DeviceVector<T>::DeviceVector(size_type count)
0139     : allocation_{count * sizeof(T)}, size_{count}
0140 {
0141 }
0142 
0143 //---------------------------------------------------------------------------//
0144 /*!
0145  * Construct with a number of allocated elements and a stream.
0146  */
0147 template<class T>
0148 DeviceVector<T>::DeviceVector(size_type count, StreamId stream)
0149     : allocation_{count * sizeof(T), stream}, size_{count}
0150 {
0151 }
0152 
0153 //---------------------------------------------------------------------------//
0154 /*!
0155  * Get the device data pointer.
0156  */
0157 template<class T>
0158 void DeviceVector<T>::swap(DeviceVector& other) noexcept
0159 {
0160     using std::swap;
0161     swap(size_, other.size_);
0162     swap(allocation_, other.allocation_);
0163 }
0164 
0165 //---------------------------------------------------------------------------//
0166 /*!
0167  * Allocate and copy from \em host pointers.
0168  *
0169  * Not exception safe: if the copy fails, the original contents are lost.
0170  */
0171 template<class T>
0172 void DeviceVector<T>::assign(T const* first, T const* last)
0173 {
0174     auto const new_size = static_cast<size_type>(last - first);
0175     if (new_size > size_ && new_size * sizeof(T) > allocation_.size())
0176     {
0177         // Reallocate
0178         *this = DeviceVector<T>(new_size, allocation_.stream_id());
0179     }
0180     else
0181     {
0182         // Update size to fit capacity
0183         size_ = new_size;
0184     }
0185 
0186     this->copy_to_device({first, new_size});
0187 }
0188 
0189 //---------------------------------------------------------------------------//
0190 /*!
0191  * Copy data to device.
0192  */
0193 template<class T>
0194 void DeviceVector<T>::copy_to_device(SpanConstT data)
0195 {
0196     CELER_EXPECT(data.size() == this->size());
0197     allocation_.copy_to_device({reinterpret_cast<std::byte const*>(data.data()),
0198                                 data.size() * sizeof(T)});
0199 }
0200 
0201 //---------------------------------------------------------------------------//
0202 /*!
0203  * Copy data to host.
0204  */
0205 template<class T>
0206 void DeviceVector<T>::copy_to_host(SpanT data) const
0207 {
0208     CELER_EXPECT(data.size() == this->size());
0209     allocation_.copy_to_host(
0210         {reinterpret_cast<std::byte*>(data.data()), data.size() * sizeof(T)});
0211 }
0212 
0213 //---------------------------------------------------------------------------//
0214 /*!
0215  * Get a device data pointer.
0216  */
0217 template<class T>
0218 T* DeviceVector<T>::data()
0219 {
0220     return reinterpret_cast<T*>(allocation_.device_ref().data());
0221 }
0222 
0223 //---------------------------------------------------------------------------//
0224 /*!
0225  * Get a device data pointer.
0226  */
0227 template<class T>
0228 T const* DeviceVector<T>::data() const
0229 {
0230     return reinterpret_cast<T const*>(allocation_.device_ref().data());
0231 }
0232 
0233 //---------------------------------------------------------------------------//
0234 /*!
0235  * Swap two vectors.
0236  */
0237 template<class T>
0238 void swap(DeviceVector<T>& a, DeviceVector<T>& b) noexcept
0239 {
0240     return a.swap(b);
0241 }
0242 
0243 //---------------------------------------------------------------------------//
0244 /*!
0245  * Prevent accidental construction of Span from a device vector.
0246  *
0247  * Use \c dv.device_ref() to get a span.
0248  */
0249 template<class T>
0250 CELER_FUNCTION Span<T const> make_span(DeviceVector<T> const& dv)
0251 {
0252     static_assert(sizeof(T) == 0, "Cannot 'make_span' from a device vector");
0253     return {dv.data(), dv.size()};
0254 }
0255 
0256 //---------------------------------------------------------------------------//
0257 //! Prevent accidental construction of Span from a device vector.
0258 template<class T>
0259 CELER_FUNCTION Span<T> make_span(DeviceVector<T>& dv)
0260 {
0261     static_assert(sizeof(T) == 0, "Cannot 'make_span' from a device vector");
0262     return {dv.data(), dv.size()};
0263 }
0264 
0265 //---------------------------------------------------------------------------//
0266 //! Create an observer pointer from a device vector.
0267 template<class T>
0268 ObserverPtr<T, MemSpace::device> make_observer(DeviceVector<T>& vec) noexcept
0269 {
0270     return ObserverPtr<T, MemSpace::device>{vec.data()};
0271 }
0272 
0273 //---------------------------------------------------------------------------//
0274 //! Create an observer pointer from a pointer in the native memspace.
0275 template<class T>
0276 ObserverPtr<T const, MemSpace::device>
0277 make_observer(DeviceVector<T> const& vec) noexcept
0278 {
0279     return ObserverPtr<T const, MemSpace::device>{vec.data()};
0280 }
0281 
0282 //---------------------------------------------------------------------------//
0283 }  // namespace celeritas