Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-08-27 08:47:19

0001 // Licensed to the Apache Software Foundation (ASF) under one
0002 // or more contributor license agreements.  See the NOTICE file
0003 // distributed with this work for additional information
0004 // regarding copyright ownership.  The ASF licenses this file
0005 // to you under the Apache License, Version 2.0 (the
0006 // "License"); you may not use this file except in compliance
0007 // with the License.  You may obtain a copy of the License at
0008 //
0009 //   http://www.apache.org/licenses/LICENSE-2.0
0010 //
0011 // Unless required by applicable law or agreed to in writing,
0012 // software distributed under the License is distributed on an
0013 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0014 // KIND, either express or implied.  See the License for the
0015 // specific language governing permissions and limitations
0016 // under the License.
0017 
0018 #pragma once
0019 
0020 #include <cstdint>
0021 #include <functional>
0022 #include <memory>
0023 #include <string>
0024 
0025 #include "arrow/io/type_fwd.h"
0026 #include "arrow/result.h"
0027 #include "arrow/status.h"
0028 #include "arrow/type_fwd.h"
0029 #include "arrow/util/compare.h"
0030 #include "arrow/util/macros.h"
0031 #include "arrow/util/visibility.h"
0032 
0033 namespace arrow {
0034 
0035 class MemoryManager;
0036 
0037 /// \brief EXPERIMENTAL: Abstract interface for hardware devices
0038 ///
0039 /// This object represents a device with access to some memory spaces.
0040 /// When handling a Buffer or raw memory address, it allows deciding in which
0041 /// context the raw memory address should be interpreted
0042 /// (e.g. CPU-accessible memory, or embedded memory on some particular GPU).
0043 class ARROW_EXPORT Device : public std::enable_shared_from_this<Device>,
0044                             public util::EqualityComparable<Device> {
0045  public:
0046   virtual ~Device();
0047 
0048   /// \brief A shorthand for this device's type.
0049   ///
0050   /// The returned value is different for each device class, but is the
0051   /// same for all instances of a given class.  It can be used as a replacement
0052   /// for RTTI.
0053   virtual const char* type_name() const = 0;
0054 
0055   /// \brief A human-readable description of the device.
0056   ///
0057   /// The returned value should be detailed enough to distinguish between
0058   /// different instances, where necessary.
0059   virtual std::string ToString() const = 0;
0060 
0061   /// \brief Whether this instance points to the same device as another one.
0062   virtual bool Equals(const Device&) const = 0;
0063 
0064   /// \brief A device ID to identify this device if there are multiple of this type.
0065   ///
0066   /// If there is no "device_id" equivalent (such as for the main CPU device on
0067   /// non-numa systems) returns -1.
0068   virtual int64_t device_id() const { return -1; }
0069 
0070   /// \brief Whether this device is the main CPU device.
0071   ///
0072   /// This shorthand method is very useful when deciding whether a memory address
0073   /// is CPU-accessible.
0074   bool is_cpu() const { return is_cpu_; }
0075 
0076   /// \brief Return a MemoryManager instance tied to this device
0077   ///
0078   /// The returned instance uses default parameters for this device type's
0079   /// MemoryManager implementation.  Some devices also allow constructing
0080   /// MemoryManager instances with non-default parameters.
0081   virtual std::shared_ptr<MemoryManager> default_memory_manager() = 0;
0082 
0083   /// \brief Return the DeviceAllocationType of this device
0084   virtual DeviceAllocationType device_type() const = 0;
0085 
0086   class SyncEvent;
0087 
0088   /// \brief EXPERIMENTAL: An opaque wrapper for Device-specific streams
0089   ///
0090   /// In essence this is just a wrapper around a void* to represent the
0091   /// standard concept of a stream/queue on a device. Derived classes
0092   /// should be trivially constructible from it's device-specific counterparts.
0093   class ARROW_EXPORT Stream {
0094    public:
0095     using release_fn_t = std::function<void(void*)>;
0096 
0097     virtual ~Stream() = default;
0098 
0099     virtual const void* get_raw() const { return stream_.get(); }
0100 
0101     /// \brief Make the stream wait on the provided event.
0102     ///
0103     /// Tells the stream that it should wait until the synchronization
0104     /// event is completed without blocking the CPU.
0105     virtual Status WaitEvent(const SyncEvent&) = 0;
0106 
0107     /// \brief Blocks the current thread until a stream's remaining tasks are completed
0108     virtual Status Synchronize() const = 0;
0109 
0110    protected:
0111     explicit Stream(void* stream, release_fn_t release_stream)
0112         : stream_{stream, release_stream} {}
0113 
0114     std::unique_ptr<void, release_fn_t> stream_;
0115   };
0116 
0117   virtual Result<std::shared_ptr<Stream>> MakeStream() { return NULLPTR; }
0118 
0119   /// \brief Create a new device stream
0120   ///
0121   /// This should create the appropriate stream type for the device,
0122   /// derived from Device::Stream to allow for stream ordered events
0123   /// and memory allocations.
0124   virtual Result<std::shared_ptr<Stream>> MakeStream(
0125       unsigned int ARROW_ARG_UNUSED(flags)) {
0126     return NULLPTR;
0127   }
0128 
0129   /// @brief Wrap an existing device stream alongside a release function
0130   ///
0131   /// @param device_stream a pointer to the stream to wrap
0132   /// @param release_fn a function to call during destruction, `nullptr` or
0133   ///        a no-op function can be passed to indicate ownership is maintained
0134   ///        externally
0135   virtual Result<std::shared_ptr<Stream>> WrapStream(
0136       void* ARROW_ARG_UNUSED(device_stream),
0137       Stream::release_fn_t ARROW_ARG_UNUSED(release_fn)) {
0138     return NULLPTR;
0139   }
0140 
0141   /// \brief EXPERIMENTAL: An object that provides event/stream sync primitives
0142   class ARROW_EXPORT SyncEvent {
0143    public:
0144     using release_fn_t = std::function<void(void*)>;
0145 
0146     virtual ~SyncEvent() = default;
0147 
0148     void* get_raw() { return sync_event_.get(); }
0149 
0150     /// @brief Block until sync event is completed.
0151     virtual Status Wait() = 0;
0152 
0153     /// @brief Record the wrapped event on the stream so it triggers
0154     /// the event when the stream gets to that point in its queue.
0155     virtual Status Record(const Stream&) = 0;
0156 
0157    protected:
0158     /// If creating this with a passed in event, the caller must ensure
0159     /// that the event lives until clear_event is called on this as it
0160     /// won't own it.
0161     explicit SyncEvent(void* sync_event, release_fn_t release_sync_event)
0162         : sync_event_{sync_event, release_sync_event} {}
0163 
0164     std::unique_ptr<void, release_fn_t> sync_event_;
0165   };
0166 
0167  protected:
0168   ARROW_DISALLOW_COPY_AND_ASSIGN(Device);
0169   explicit Device(bool is_cpu = false) : is_cpu_(is_cpu) {}
0170 
0171   bool is_cpu_;
0172 };
0173 
0174 /// \brief EXPERIMENTAL: An object that provides memory management primitives
0175 ///
0176 /// A MemoryManager is always tied to a particular Device instance.
0177 /// It can also have additional parameters (such as a MemoryPool to
0178 /// allocate CPU memory).
0179 class ARROW_EXPORT MemoryManager : public std::enable_shared_from_this<MemoryManager> {
0180  public:
0181   virtual ~MemoryManager();
0182 
0183   /// \brief The device this MemoryManager is tied to
0184   const std::shared_ptr<Device>& device() const { return device_; }
0185 
0186   /// \brief Whether this MemoryManager is tied to the main CPU device.
0187   ///
0188   /// This shorthand method is very useful when deciding whether a memory address
0189   /// is CPU-accessible.
0190   bool is_cpu() const { return device_->is_cpu(); }
0191 
0192   /// \brief Create a RandomAccessFile to read a particular buffer.
0193   ///
0194   /// The given buffer must be tied to this MemoryManager.
0195   ///
0196   /// See also the Buffer::GetReader shorthand.
0197   virtual Result<std::shared_ptr<io::RandomAccessFile>> GetBufferReader(
0198       std::shared_ptr<Buffer> buf) = 0;
0199 
0200   /// \brief Create a OutputStream to write to a particular buffer.
0201   ///
0202   /// The given buffer must be mutable and tied to this MemoryManager.
0203   /// The returned stream object writes into the buffer's underlying memory
0204   /// (but it won't resize it).
0205   ///
0206   /// See also the Buffer::GetWriter shorthand.
0207   virtual Result<std::shared_ptr<io::OutputStream>> GetBufferWriter(
0208       std::shared_ptr<Buffer> buf) = 0;
0209 
0210   /// \brief Allocate a (mutable) Buffer
0211   ///
0212   /// The buffer will be allocated in the device's memory.
0213   virtual Result<std::unique_ptr<Buffer>> AllocateBuffer(int64_t size) = 0;
0214 
0215   /// \brief Copy a Buffer to a destination MemoryManager
0216   ///
0217   /// See also the Buffer::Copy shorthand.
0218   static Result<std::shared_ptr<Buffer>> CopyBuffer(
0219       const std::shared_ptr<Buffer>& source, const std::shared_ptr<MemoryManager>& to);
0220 
0221   /// \brief Copy a non-owned Buffer to a destination MemoryManager
0222   ///
0223   /// This is useful for cases where the source memory area is externally managed
0224   /// (its lifetime not tied to the source Buffer), otherwise please use CopyBuffer().
0225   static Result<std::unique_ptr<Buffer>> CopyNonOwned(
0226       const Buffer& source, const std::shared_ptr<MemoryManager>& to);
0227 
0228   /// \brief Make a no-copy Buffer view in a destination MemoryManager
0229   ///
0230   /// See also the Buffer::View shorthand.
0231   static Result<std::shared_ptr<Buffer>> ViewBuffer(
0232       const std::shared_ptr<Buffer>& source, const std::shared_ptr<MemoryManager>& to);
0233 
0234   /// \brief Copy a slice of a buffer into a CPU pointer
0235   static Status CopyBufferSliceToCPU(const std::shared_ptr<Buffer>& buf, int64_t offset,
0236                                      int64_t length, uint8_t* out_data);
0237 
0238   /// \brief Create a new SyncEvent.
0239   ///
0240   /// This version should construct the appropriate event for the device and
0241   /// provide the unique_ptr with the correct deleter for the event type.
0242   /// If the device does not require or work with any synchronization, it is
0243   /// allowed for it to return a nullptr.
0244   virtual Result<std::shared_ptr<Device::SyncEvent>> MakeDeviceSyncEvent();
0245 
0246   /// \brief Wrap an event into a SyncEvent.
0247   ///
0248   /// @param sync_event passed in sync_event (should be a pointer to the appropriate type)
0249   /// @param release_sync_event destructor to free sync_event. `nullptr` may be
0250   ///        passed to indicate that no destruction/freeing is necessary
0251   virtual Result<std::shared_ptr<Device::SyncEvent>> WrapDeviceSyncEvent(
0252       void* sync_event, Device::SyncEvent::release_fn_t release_sync_event);
0253 
0254  protected:
0255   ARROW_DISALLOW_COPY_AND_ASSIGN(MemoryManager);
0256 
0257   explicit MemoryManager(const std::shared_ptr<Device>& device) : device_(device) {}
0258 
0259   // Default implementations always return nullptr, should be overridden
0260   // by subclasses that support data transfer.
0261   // (returning nullptr means unsupported copy / view)
0262   // In CopyBufferFrom and ViewBufferFrom, the `from` parameter is guaranteed to
0263   // be equal to `buf->memory_manager()`.
0264   virtual Result<std::shared_ptr<Buffer>> CopyBufferFrom(
0265       const std::shared_ptr<Buffer>& buf, const std::shared_ptr<MemoryManager>& from);
0266   virtual Result<std::shared_ptr<Buffer>> CopyBufferTo(
0267       const std::shared_ptr<Buffer>& buf, const std::shared_ptr<MemoryManager>& to);
0268   virtual Result<std::unique_ptr<Buffer>> CopyNonOwnedFrom(
0269       const Buffer& buf, const std::shared_ptr<MemoryManager>& from);
0270   virtual Result<std::unique_ptr<Buffer>> CopyNonOwnedTo(
0271       const Buffer& buf, const std::shared_ptr<MemoryManager>& to);
0272   virtual Result<std::shared_ptr<Buffer>> ViewBufferFrom(
0273       const std::shared_ptr<Buffer>& buf, const std::shared_ptr<MemoryManager>& from);
0274   virtual Result<std::shared_ptr<Buffer>> ViewBufferTo(
0275       const std::shared_ptr<Buffer>& buf, const std::shared_ptr<MemoryManager>& to);
0276 
0277   std::shared_ptr<Device> device_;
0278 };
0279 
0280 // ----------------------------------------------------------------------
0281 // CPU backend implementation
0282 
0283 class ARROW_EXPORT CPUDevice : public Device {
0284  public:
0285   const char* type_name() const override;
0286   std::string ToString() const override;
0287   bool Equals(const Device&) const override;
0288   DeviceAllocationType device_type() const override { return DeviceAllocationType::kCPU; }
0289 
0290   std::shared_ptr<MemoryManager> default_memory_manager() override;
0291 
0292   /// \brief Return the global CPUDevice instance
0293   static std::shared_ptr<Device> Instance();
0294 
0295   /// \brief Create a MemoryManager
0296   ///
0297   /// The returned MemoryManager will use the given MemoryPool for allocations.
0298   static std::shared_ptr<MemoryManager> memory_manager(MemoryPool* pool);
0299 
0300  protected:
0301   CPUDevice() : Device(true) {}
0302 };
0303 
0304 class ARROW_EXPORT CPUMemoryManager : public MemoryManager {
0305  public:
0306   Result<std::shared_ptr<io::RandomAccessFile>> GetBufferReader(
0307       std::shared_ptr<Buffer> buf) override;
0308   Result<std::shared_ptr<io::OutputStream>> GetBufferWriter(
0309       std::shared_ptr<Buffer> buf) override;
0310 
0311   Result<std::unique_ptr<Buffer>> AllocateBuffer(int64_t size) override;
0312 
0313   /// \brief Return the MemoryPool associated with this MemoryManager.
0314   MemoryPool* pool() const { return pool_; }
0315 
0316  protected:
0317   CPUMemoryManager(const std::shared_ptr<Device>& device, MemoryPool* pool)
0318       : MemoryManager(device), pool_(pool) {}
0319 
0320   static std::shared_ptr<MemoryManager> Make(const std::shared_ptr<Device>& device,
0321                                              MemoryPool* pool = default_memory_pool());
0322 
0323   Result<std::shared_ptr<Buffer>> CopyBufferFrom(
0324       const std::shared_ptr<Buffer>& buf,
0325       const std::shared_ptr<MemoryManager>& from) override;
0326   Result<std::shared_ptr<Buffer>> CopyBufferTo(
0327       const std::shared_ptr<Buffer>& buf,
0328       const std::shared_ptr<MemoryManager>& to) override;
0329   Result<std::unique_ptr<Buffer>> CopyNonOwnedFrom(
0330       const Buffer& buf, const std::shared_ptr<MemoryManager>& from) override;
0331   Result<std::unique_ptr<Buffer>> CopyNonOwnedTo(
0332       const Buffer& buf, const std::shared_ptr<MemoryManager>& to) override;
0333   Result<std::shared_ptr<Buffer>> ViewBufferFrom(
0334       const std::shared_ptr<Buffer>& buf,
0335       const std::shared_ptr<MemoryManager>& from) override;
0336   Result<std::shared_ptr<Buffer>> ViewBufferTo(
0337       const std::shared_ptr<Buffer>& buf,
0338       const std::shared_ptr<MemoryManager>& to) override;
0339 
0340   MemoryPool* pool_;
0341 
0342   friend std::shared_ptr<MemoryManager> CPUDevice::memory_manager(MemoryPool* pool);
0343   ARROW_FRIEND_EXPORT friend std::shared_ptr<MemoryManager> default_cpu_memory_manager();
0344 };
0345 
0346 /// \brief Return the default CPU MemoryManager instance
0347 ///
0348 /// The returned singleton instance uses the default MemoryPool.
0349 /// This function is a faster spelling of
0350 /// `CPUDevice::Instance()->default_memory_manager()`.
0351 ARROW_EXPORT
0352 std::shared_ptr<MemoryManager> default_cpu_memory_manager();
0353 
0354 using DeviceMapper =
0355     std::function<Result<std::shared_ptr<MemoryManager>>(int64_t device_id)>;
0356 
0357 /// \brief Register a function to retrieve a MemoryManager for a Device type
0358 ///
0359 /// This registers the device type globally. A specific device type can only
0360 /// be registered once. This method is thread-safe.
0361 ///
0362 /// Currently, this registry is only used for importing data through the C Device
0363 /// Data Interface (for the default Device to MemoryManager mapper in
0364 /// arrow::ImportDeviceArray/ImportDeviceRecordBatch).
0365 ///
0366 /// \param[in] device_type the device type for which to register a MemoryManager
0367 /// \param[in] mapper function that takes a device id and returns the appropriate
0368 /// MemoryManager for the registered device type and given device id
0369 /// \return Status
0370 ARROW_EXPORT
0371 Status RegisterDeviceMapper(DeviceAllocationType device_type, DeviceMapper mapper);
0372 
0373 /// \brief Get the registered function to retrieve a MemoryManager for the
0374 /// given Device type
0375 ///
0376 /// \param[in] device_type the device type
0377 /// \return function that takes a device id and returns the appropriate
0378 /// MemoryManager for the registered device type and given device id
0379 ARROW_EXPORT
0380 Result<DeviceMapper> GetDeviceMapper(DeviceAllocationType device_type);
0381 
0382 }  // namespace arrow