Skip to content

Commit 8dc62fc

Browse files
committed
[ET Device Support] DeviceAllocator interface and DeviceAllocatorRegistry
This diff introduces the `DeviceAllocator` abstract interface and `DeviceAllocatorRegistry` for device-specific memory allocation. This is a foundational abstraction that enables the runtime to dispatch memory operations to the appropriate device backend other than CPU (CUDA, etc.). **DeviceAllocator interface provides:** - `init_buffer()` - Initialize memory buffer pools for memory-planned tensors - `get_offset_address()` - Get pointer to offset within pre-allocated buffer - `allocate()` / `deallocate()` - Dynamic device memory allocation - `copy_host_to_device()` / `copy_device_to_host()` - Data transfer between host and device - `device_type()` - Returns the device type this allocator handles **DeviceAllocatorRegistry provides:** - Singleton registry mapping DeviceType → DeviceAllocator - `register_allocator()` / `get_allocator()` methods - Fixed-size array indexed by device type (no dynamic allocation, embedded-friendly) **Design notes:** - Registry stores raw pointers (non-owning) - allocators are expected to be singletons with static lifetime - Follows ExecuTorch's embedded-first philosophy (no std::unique_ptr, no heap allocation in registry) - Convenience free functions `register_device_allocator()` and `get_device_allocator()` for ease of use Differential Revision: [D93635656](https://our.internmc.facebook.com/intern/diff/D93635656/) ghstack-source-id: 342367956 Pull Request resolved: #17535
1 parent 91f2c3a commit 8dc62fc

3 files changed

Lines changed: 514 additions & 0 deletions

File tree

runtime/core/device_allocator.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#include <executorch/runtime/core/device_allocator.h>
10+
11+
#include <executorch/runtime/platform/assert.h>
12+
13+
namespace executorch {
14+
namespace runtime {
15+
16+
DeviceAllocatorRegistry& DeviceAllocatorRegistry::instance() {
17+
static DeviceAllocatorRegistry registry;
18+
return registry;
19+
}
20+
21+
void DeviceAllocatorRegistry::register_allocator(
22+
etensor::DeviceType type,
23+
DeviceAllocator* alloc) {
24+
auto index = static_cast<size_t>(type);
25+
ET_CHECK_MSG(
26+
index < etensor::kNumDeviceTypes,
27+
"Invalid device type: %d",
28+
static_cast<int>(type));
29+
ET_CHECK_MSG(
30+
allocators_[index] == nullptr,
31+
"Allocator already registered for device type: %d",
32+
static_cast<int>(type));
33+
allocators_[index] = alloc;
34+
}
35+
36+
DeviceAllocator* DeviceAllocatorRegistry::get_allocator(
37+
etensor::DeviceType type) {
38+
auto index = static_cast<size_t>(type);
39+
if (index >= etensor::kNumDeviceTypes) {
40+
return nullptr;
41+
}
42+
return allocators_[index];
43+
}
44+
45+
// Convenience free functions
46+
47+
void register_device_allocator(
48+
etensor::DeviceType type,
49+
DeviceAllocator* alloc) {
50+
DeviceAllocatorRegistry::instance().register_allocator(type, alloc);
51+
}
52+
53+
DeviceAllocator* get_device_allocator(etensor::DeviceType type) {
54+
return DeviceAllocatorRegistry::instance().get_allocator(type);
55+
}
56+
57+
} // namespace runtime
58+
} // namespace executorch

runtime/core/device_allocator.h

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#pragma once
10+
11+
#include <cstddef>
12+
#include <cstdint>
13+
14+
#include <executorch/runtime/core/error.h>
15+
#include <executorch/runtime/core/portable_type/device.h>
16+
#include <executorch/runtime/core/result.h>
17+
18+
namespace executorch {
19+
namespace runtime {
20+
21+
/**
22+
* Abstract interface for device-specific memory allocation.
23+
*
24+
* Each device type (CUDA, etc.) provides a concrete implementation
25+
* that handles memory allocation on that device. Implementations are
26+
* expected to be singletons with static lifetime, registered via
27+
* DeviceAllocatorRegistry.
28+
29+
*/
30+
class DeviceAllocator {
31+
public:
32+
virtual ~DeviceAllocator() = default;
33+
34+
/**
35+
* Initialize a memory buffer pool for memory-planned tensors.
36+
*
37+
* @param memory_id The ID of the memory buffer (index into
38+
* ExecutionPlan.non_const_buffer_sizes).
39+
* @param size The size in bytes to allocate for this buffer.
40+
* @param index The device index (e.g., GPU 0 vs GPU 1).
41+
* @return Error::Ok on success, or an appropriate error code on failure.
42+
*/
43+
virtual Error
44+
init_buffer(uint32_t memory_id, size_t size, etensor::DeviceIndex index) = 0;
45+
46+
/**
47+
* Get a pointer to a specific offset within a pre-allocated buffer pool.
48+
*
49+
* @param memory_id The ID of the memory buffer.
50+
* @param offset_bytes Offset in bytes from the start of the buffer.
51+
* @param size_bytes Size of the requested region in bytes.
52+
* @param index The device index.
53+
* @return A Result containing the device pointer on success, or an error.
54+
*/
55+
virtual Result<void*> get_offset_address(
56+
uint32_t memory_id,
57+
size_t offset_bytes,
58+
size_t size_bytes,
59+
etensor::DeviceIndex index) = 0;
60+
61+
/**
62+
* Allocate device memory.
63+
*
64+
* @param nbytes Number of bytes to allocate.
65+
* @param index The device index.
66+
* @return A Result containing the device pointer on success, or an error.
67+
*/
68+
virtual Result<void*> allocate(size_t nbytes, etensor::DeviceIndex index) = 0;
69+
70+
/**
71+
* Deallocate device memory previously allocated via allocate().
72+
*
73+
* @param ptr Pointer to the memory to deallocate.
74+
* @param index The device index.
75+
*/
76+
virtual void deallocate(void* ptr, etensor::DeviceIndex index) = 0;
77+
78+
/**
79+
* Copy data from host memory to device memory.
80+
*
81+
* @param dst Destination pointer (device memory).
82+
* @param src Source pointer (host memory).
83+
* @param nbytes Number of bytes to copy.
84+
* @param index The device index.
85+
* @return Error::Ok on success, or an appropriate error code on failure.
86+
*/
87+
virtual Error copy_host_to_device(
88+
void* dst,
89+
const void* src,
90+
size_t nbytes,
91+
etensor::DeviceIndex index) = 0;
92+
93+
/**
94+
* Copy data from device memory to host memory.
95+
*
96+
* @param dst Destination pointer (host memory).
97+
* @param src Source pointer (device memory).
98+
* @param nbytes Number of bytes to copy.
99+
* @param index The device index.
100+
* @return Error::Ok on success, or an appropriate error code on failure.
101+
*/
102+
virtual Error copy_device_to_host(
103+
void* dst,
104+
const void* src,
105+
size_t nbytes,
106+
etensor::DeviceIndex index) = 0;
107+
108+
/**
109+
* Returns the device type this allocator handles.
110+
*/
111+
virtual etensor::DeviceType device_type() const = 0;
112+
};
113+
114+
/**
115+
* Registry for device allocators.
116+
*
117+
* Provides a global mapping from DeviceType to DeviceAllocator instances.
118+
* Device allocators register themselves at static initialization time,
119+
* and the runtime queries the registry to find the appropriate allocator
120+
* for a given device type.
121+
*/
122+
class DeviceAllocatorRegistry {
123+
public:
124+
/**
125+
* Returns the singleton instance of the registry.
126+
*/
127+
static DeviceAllocatorRegistry& instance();
128+
129+
/**
130+
* Register an allocator for a specific device type.
131+
*
132+
* @param type The device type this allocator handles.
133+
* @param alloc Pointer to the allocator (must have static lifetime).
134+
*/
135+
void register_allocator(etensor::DeviceType type, DeviceAllocator* alloc);
136+
137+
/**
138+
* Get the allocator for a specific device type.
139+
*
140+
* @param type The device type.
141+
* @return Pointer to the allocator, or nullptr if not registered.
142+
*/
143+
DeviceAllocator* get_allocator(etensor::DeviceType type);
144+
145+
private:
146+
DeviceAllocatorRegistry() = default;
147+
148+
// Fixed-size array indexed by device type. This avoids dynamic allocation
149+
// and is suitable for embedded environments.
150+
DeviceAllocator* allocators_[etensor::kNumDeviceTypes] = {};
151+
};
152+
153+
// Convenience free functions
154+
155+
/**
156+
* Register a device allocator for a specific device type.
157+
*
158+
* @param type The device type this allocator handles.
159+
* @param alloc Pointer to the allocator (must have static lifetime).
160+
*/
161+
void register_device_allocator(
162+
etensor::DeviceType type,
163+
DeviceAllocator* alloc);
164+
165+
/**
166+
* Get the device allocator for a specific device type.
167+
*
168+
* @param type The device type.
169+
* @return Pointer to the allocator, or nullptr if not registered.
170+
*/
171+
DeviceAllocator* get_device_allocator(etensor::DeviceType type);
172+
173+
} // namespace runtime
174+
} // namespace executorch
175+
176+
namespace torch {
177+
namespace executor {
178+
// TODO(T197294990): Remove these deprecated aliases once all users have moved
179+
// to the new `::executorch` namespaces.
180+
using ::executorch::runtime::DeviceAllocator;
181+
using ::executorch::runtime::DeviceAllocatorRegistry;
182+
using ::executorch::runtime::get_device_allocator;
183+
using ::executorch::runtime::register_device_allocator;
184+
} // namespace executor
185+
} // namespace torch

0 commit comments

Comments
 (0)