Skip to content

Commit dc86744

Browse files
shawnwang18tensorflower-gardener
authored andcommitted
PR tensorflow#44155: [XLA:GPU] Move VMM allocator API comments
Imported from GitHub PR openxla/xla#44155 📝 Summary of Changes Moves API documentation from tensorflow#43400 for declarations that already exist after tensorflow#44121. This is comment-only and stacked on tensorflow#44121. 🎯 Justification Keeps the rename PR small while making the existing VMM allocator API documentation clearer before later behavior changes. 🚀 Kind of Contribution 📚 Documentation 📊 Benchmark (for Performance Improvements) N/A. 🧪 Unit Tests: N/A. Comment-only change. 🧪 Execution Tests: N/A. Comment-only change. Copybara import of the project: -- 613c866d9b808d5be43f742305708dc613cec569 by Shawn Wang <shawnw@nvidia.com>: Rename VMM device address allocator files -- 1e79f19bd5c74db32e5e86ade6b607d211b555a3 by Shawn Wang <shawnw@nvidia.com>: Move VMM allocator API comments -- f73a110271069f6ea069cd4c7df915ea82972b5e by Shawn Wang <shawnw@nvidia.com>: Add NOLINT markers for VMM allocator comment table Merging this change closes tensorflow#44155 PiperOrigin-RevId: 933856458
1 parent f3f3323 commit dc86744

1 file changed

Lines changed: 129 additions & 48 deletions

File tree

third_party/xla/xla/stream_executor/device_address_vmm_allocator.h

Lines changed: 129 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -42,41 +42,89 @@ class DeviceAssignment;
4242

4343
namespace stream_executor {
4444

45-
// Abstract base class for virtual memory map (VMM) allocators that separate
46-
// virtual address reservation from physical memory allocation. It can be bound
47-
// to one or more GPU devices at construction time and routes
48-
// Allocate/Deallocate calls to per-device state based on device_ordinal.
45+
// Abstract base class for a DeviceAddressAllocator backed by virtual memory
46+
// management (VMM). VMM lets the allocator manage device memory in three
47+
// separate steps:
4948
//
50-
// Concurrency model: each device has its own absl::Mutex, so operations on
51-
// different devices run fully in parallel. The per-device map is populated
52-
// entirely at construction and never modified afterward, so lookups require no
53-
// global lock.
49+
// 1. Allocate raw physical memory. This is the real device memory capacity.
50+
// 2. Reserve a virtual address (VA) range. This creates addresses but does not
51+
// make them usable yet.
52+
// 3. Map a VA range to raw physical memory. Device kernels access memory
53+
// through the mapped VA.
5454
//
55-
// For each device the allocator:
56-
// 1. Allocates physical memory via MemoryAllocation (e.g. cuMemCreate).
57-
// 2. Reserves virtual address space via MemoryReservation
58-
// (e.g. cuMemAddressReserve).
59-
// 3. Maps physical memory to virtual address via
60-
// MemoryReservation::ScopedMapping (e.g. cuMemMap + cuMemSetAccess), which
61-
// automatically unmaps on destruction.
55+
// A concrete subclass provides the platform-specific operations for those
56+
// steps, plus a stream-ordered timeline used to know when old mappings and
57+
// allocations are safe to release.
6258
//
63-
// The allocator tracks the ScopedMapping and underlying MemoryAllocation and
64-
// MemoryReservation objects for each returned DeviceAddressBase. Callers can
65-
// retrieve these via GetRawAllocation() and GetReservation(). Callers can also
66-
// create non-owning aliases into caller-owned MemoryReservation ranges with
67-
// Map(), then release those aliases with UnMap().
59+
// Caller-visible address roles:
6860
//
69-
// This allocator supports asynchronous deallocation: when Deallocate() is
70-
// called, it records a GPU timeline write on the device's stream and defers
71-
// the actual deallocation until the GPU reaches that point in the stream. This
72-
// allows callers to deallocate memory while device kernels may still be
73-
// consuming the data.
61+
// * Allocator address: any address returned by Allocate(). It owns the raw
62+
// physical allocation, can be used as the source address for Map(), and must
63+
// eventually be released with Deallocate().
64+
// * Reservation address: a caller-owned MemoryReservation slice
65+
// [reservation_base + offset, reservation_base + offset + size) that is
66+
// mapped as a non-owning alias of an allocator address. It must be released
67+
// with UnMap(), not Deallocate().
68+
//
69+
// clang-format off
70+
// NOLINTBEGIN(whitespace/line_length)
71+
// Allowed address behavior:
72+
//
73+
// +--------------------------------------------------------+---------------------+------------+-----+-------+
74+
// | Address | Role | Deallocate | Map | UnMap |
75+
// +--------------------------------------------------------+---------------------+------------+-----+-------+
76+
// | Allocate() return | allocator address | yes | yes | no |
77+
// | Allocate(..., return_reservation_address=true) return | allocator address | yes | yes | no |
78+
// | Allocate(..., return_reservation_address=false) return | allocator address | yes | yes | no |
79+
// | reservation slice from Allocate(..., false) | reservation address | no | no | yes |
80+
// | reservation slice from Map() | reservation address | no | no | yes |
81+
// +--------------------------------------------------------+---------------------+------------+-----+-------+
82+
// NOLINTEND(whitespace/line_length)
83+
// clang-format on
84+
//
85+
// The table uses "yes" for API calls that accept the address in that row. For
86+
// example, Map() takes an allocator address as its source, while UnMap() takes
87+
// a reservation address to tear down. Map() still requires the allocator
88+
// address to have no active reservation-address alias; for example, an
89+
// Allocate(..., return_reservation_address=false) result can be remapped only
90+
// after its initial reservation-address alias is released with UnMap().
91+
//
92+
// The main API flows are:
93+
//
94+
// 1. Allocate(size) creates an allocator-owned VA reservation, allocates raw
95+
// physical memory, maps that memory into the owned reservation, and returns
96+
// the allocator address.
97+
// 2. Allocate(..., return_reservation_address=true) allocates raw physical
98+
// memory and maps it directly into the caller reservation. The returned VA
99+
// comes from the caller reservation, but it is still the allocator address
100+
// for this allocation.
101+
// 3. Allocate(..., return_reservation_address=false) returns a separate
102+
// allocator-owned address and also maps the same raw physical allocation
103+
// into the caller reservation as a reservation address.
104+
// 4. Map(addr, reservation, ...) maps the raw physical allocation currently
105+
// backing allocator address `addr` into one caller reservation slice.
106+
// UnMap(reservation, ...) removes that reservation-address alias.
107+
//
108+
// Deallocate() accepts only allocator addresses and requires any active
109+
// reservation-address alias to be released with UnMap() first. UnMap() accepts
110+
// only reservation addresses created by Map() or by
111+
// Allocate(..., return_reservation_address=false). Passing an allocator address
112+
// to UnMap(), or a reservation address to Deallocate(), is an error.
113+
// Each allocator address may have at most one active reservation-address alias.
114+
//
115+
// Deallocate() and UnMap() are stream-ordered deferred operations. The
116+
// allocator records a per-device sequence number for the affected address and
117+
// keeps the old mapping or allocation alive until the stream reaches that
118+
// sequence number, so kernels already submitted to the stream can keep using
119+
// the old VA. When the sequence completes, dropping the ScopedMapping objects
120+
// performs the real unmap, then the allocator releases any owned reservation
121+
// and raw physical memory.
74122
//
75123
// Concrete subclasses implement the platform-specific virtual methods
76124
// (InitializeDeviceState, CreateAllocation, CreateReservation,
77125
// EnqueueDeferredDeallocation) and expose platform-specific Create() factories.
78126
// Subclasses must also set PerDeviceState::destroy_fn in InitializeDeviceState
79-
// to release platform-specific resources (e.g. pinned timeline memory).
127+
// to release platform-specific resources such as pinned timeline memory.
80128
//
81129
// This allocator is thread-safe for concurrent use by multiple threads across
82130
// any registered devices.
@@ -100,15 +148,24 @@ class DeviceAddressVmmAllocator : public DeviceAddressAllocator {
100148
int64_t memory_space) override;
101149

102150
// Allocates raw physical memory and maps it into a caller-owned
103-
// MemoryReservation range. `allocation_size` and `mapping_size` must be
104-
// equal.
151+
// MemoryReservation range.
152+
// `allocation_size` and `mapping_size` must be equal.
153+
//
154+
// There are two modes:
155+
//
156+
// * `return_reservation_address=true`: the mapped reservation slice is
157+
// returned and is treated as the allocator address. The caller releases it
158+
// with Deallocate(), may use it as a Map() source, and must not pass it to
159+
// UnMap().
160+
// * `return_reservation_address=false`: the allocator creates and returns a
161+
// separate allocator-owned address. The same raw physical allocation is
162+
// also mapped into the caller reservation as a reservation address. The
163+
// returned allocator address is released with Deallocate(); the
164+
// reservation-address alias may be released earlier with UnMap().
105165
//
106-
// If `return_reservation_address` is true, the returned allocator address is
107-
// the reservation slice and must be released with Deallocate(); `reservation`
108-
// must outlive the returned address and any pending deallocation. If false,
109-
// the returned allocator address is a separate allocator-owned VA and the
110-
// reservation slice is a non-owning alias that must be released with UnMap()
111-
// before the returned allocator address is deallocated.
166+
// The caller owns `reservation` and must keep it alive while any mapping into
167+
// it is active or waiting for deferred unmap completion. Deallocate() never
168+
// destroys or takes ownership of `reservation`.
112169
absl::StatusOr<ScopedDeviceAddress<uint8_t>> Allocate(
113170
int device_ordinal, uint64_t allocation_size, bool retry_on_failure,
114171
int64_t memory_space, MemoryReservation* reservation,
@@ -132,34 +189,58 @@ class DeviceAddressVmmAllocator : public DeviceAddressAllocator {
132189
const xla::DeviceAssignment* previous_;
133190
};
134191

135-
// Deallocates memory asynchronously. The caller can call this function even
136-
// if device kernels are still consuming the data — the actual deallocation
137-
// will be deferred until all previously enqueued work on the device's stream
138-
// completes.
192+
// Deallocates an allocator address asynchronously. `mem` must be an address
193+
// returned by Allocate(), including reservation-derived addresses returned by
194+
// Allocate(..., return_reservation_address=true). Reservation addresses
195+
// created by Map() or by Allocate(..., return_reservation_address=false) must
196+
// not be passed to Deallocate(). If `mem` has an active reservation-address
197+
// alias, the caller must release that alias with UnMap() before calling
198+
// Deallocate(). The caller can call this function while device kernels are
199+
// still consuming the data; the actual release is deferred until earlier work
200+
// on the device stream completes.
139201
absl::Status Deallocate(int device_ordinal, DeviceAddressBase mem) override;
140202

141-
// Maps the physical allocation backing `addr` into `reservation` at
142-
// `reservation_offset`. `addr` must be an active allocator address returned
143-
// by Allocate(), and each allocator address may have at most one active
144-
// reservation alias.
203+
// Adds a reservation-address alias for an existing allocator address by
204+
// mapping the physical allocation currently backing `addr` into
205+
// `reservation` at `reservation_offset`.
206+
//
207+
// `addr` must be an active allocator address returned by this allocator,
208+
// including reservation-derived addresses returned by
209+
// Allocate(..., return_reservation_address=true). Non-owning reservation
210+
// addresses created by Map() or by
211+
// Allocate(..., return_reservation_address=false), and addresses from other
212+
// allocators, are not supported. The physical allocation backing `addr` must
213+
// be at least `size` bytes. Each allocator address may have at most one
214+
// active reservation-address alias at a time. The caller owns `reservation`
215+
// and must keep it alive until UnMap() is called and the allocator stream
216+
// reaches that deferred unmap point.
145217
absl::Status Map(int device_ordinal, DeviceAddressBase addr,
146218
MemoryReservation* reservation, uint64_t reservation_offset,
147219
uint64_t size);
148220

149-
// Defers unmapping the reservation alias created by Map() until all
150-
// previously enqueued work on this allocator's stream has completed. The
151-
// caller must pass the same full reservation range used for Map().
221+
// Defers unmapping the reservation address created by Map() or by
222+
// Allocate(..., return_reservation_address=false) for the given reservation
223+
// range until all previously enqueued work on the allocator stream has
224+
// completed.
225+
// The caller must pass the same full reservation range that created the
226+
// mapping. The reservation-derived allocator address returned by
227+
// Allocate(..., return_reservation_address=true) is not a reservation
228+
// address for this API and must be released with Deallocate() instead.
229+
//
230+
// On success this method moves the active mapping to the deferred unmap
231+
// queue. On error, active bookkeeping is unchanged. Empty mappings, such as
232+
// zero-size Map(), are treated as no-ops.
152233
absl::Status UnMap(int device_ordinal, MemoryReservation* reservation,
153234
uint64_t reservation_offset, uint64_t size);
154235

155-
// Returns true this allocator supports asynchronous deallocation.
236+
// Returns true: this allocator supports asynchronous deallocation.
156237
bool AllowsAsynchronousDeallocation() const override { return true; }
157238

158239
// Returns the stream for the given device ordinal.
159240
absl::StatusOr<Stream*> GetStream(int device_ordinal) override;
160241

161-
// Waits for all pending stream-ordered deallocations on the given device to
162-
// complete, then releases the corresponding allocator bookkeeping.
242+
// Waits for all pending stream-ordered deallocations and unmaps on the given
243+
// device to complete, then drops the corresponding deferred bookkeeping.
163244
absl::Status SynchronizePendingOperations(int device_ordinal);
164245

165246
// Returns the StreamExecutor for the given device ordinal.

0 commit comments

Comments
 (0)