xref: /aosp_15_r20/external/tensorflow/tensorflow/compiler/xla/stream_executor/temporary_memory_manager.h (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 // The temporary-memory-manager is a helper class for a Stream to keep track of
17 // temporary allocations. These allocations defer their deallocation to the next
18 // Stream::BlockHostUntilDone call for efficiency purposes (as deallocation
19 // itself generally forces synchronization to occur).
20 
21 #ifndef TENSORFLOW_COMPILER_XLA_STREAM_EXECUTOR_TEMPORARY_MEMORY_MANAGER_H_
22 #define TENSORFLOW_COMPILER_XLA_STREAM_EXECUTOR_TEMPORARY_MEMORY_MANAGER_H_
23 
24 #include <map>
25 #include <memory>
26 #include <utility>
27 
28 #include "absl/base/thread_annotations.h"
29 #include "absl/synchronization/mutex.h"
30 #include "tensorflow/compiler/xla/stream_executor/device_memory.h"
31 #include "tensorflow/compiler/xla/stream_executor/lib/status.h"
32 #include "tensorflow/compiler/xla/stream_executor/lib/statusor.h"
33 #include "tensorflow/compiler/xla/stream_executor/temporary_device_memory.h"
34 
35 namespace stream_executor {
36 namespace internal {
37 
38 // Record used inside the TemporaryMemoryManager as metadata for a given device
39 // memory region.
40 struct TemporaryMemoryRecord {
41   // What "generation" this record was allocated in.
42   //
43   // Currently the generation counter is bumped for every allocation, but this
44   // could be made coarser if necessary.
45   uint64_t allocation_generation;
46 
47   // Notes whether the temporary memory has been marked as finalized, such that
48   // we can release the DeviceMemory associated with this record at
49   // synchronization time.
50   bool finalized;
51 };
52 
53 // Manages temporary memories associated with a stream -- keeps records of
54 // outstanding temporaries and their state, and can deallocate them
55 // appropriately at points in the Stream lifecycle (e.g. BlockHostUntilDone,
56 // destruction).
57 class TemporaryMemoryManager {
58  public:
TemporaryMemoryManager(Stream * stream)59   explicit TemporaryMemoryManager(Stream* stream) : stream_(stream) {}
60 
61   // Allocates a temporary array that is then managed by this object.
62   template <typename T>
63   port::StatusOr<std::unique_ptr<TemporaryDeviceMemory<T>>> AllocateArray(
64       uint64_t element_count);
65 
66   // Forces deallocation of all managed temporary memory regions.
67   //
68   // Called, for example, when the Stream owning this temporary memory manager
69   // is destroyed.
70   //
71   // Note: These calls to Deallocate will likely force synchronization.
72   void ForceDeallocateAll();
73 
74   // Marks the given memory region as finalized.
75   //
76   // If must_exist is set, this will check-fail if the temporary memory record
77   // is not found.
78   void MarkFinalized(const DeviceMemoryBase& device_memory, uint64_t generation,
79                      bool must_exist);
80 
81   // Deallocates temporary memories that have been finalized.
82   //
83   // Note: These calls to Deallocate will likely force synchronization, so it is
84   // meant to be called before a "BlockHostUntilDone" is about to be performed.
85   void DeallocateFinalizedTemporaries();
86 
87   // Returns whether the provided device_memory is finalized.
88   //
89   // In the vacuous case where the device memory doesn't appear in the temporary
90   // memory records, it is either not a temporary at all, or has already been
91   // deallocated, and thus returns true.
92   bool IsFinalized(const DeviceMemoryBase& device_memory,
93                    uint64_t allocation_generation) const;
94 
95   // Returns whether the manager has a live allocation record for the given
96   // device memory pointer with the given generation counter.
97   //
98   // Note: this is a polling call -- there is no guarantee that the region is
99   // still allocated once the call has completed.
100   bool HasAllocated(const DeviceMemoryBase& device_memory,
101                     uint64_t generation) const;
102 
103  private:
104   // Allocates an array without type parameterization, so that the
105   // implementation can live in the source file. Without this base allocation
106   // method, we incur a circular dependency between the StreamExecutor
107   // definition and this class' definition.
108   port::StatusOr<std::unique_ptr<TemporaryDeviceMemoryBase>> AllocateArrayBase(
109       uint64_t element_count, uint64 element_size);
110 
111   // Mutex to guard temporary record state.
112   mutable absl::Mutex mutex_;
113 
114   // Mapping from device memory to the current (live) temporary memory record.
115   //
116   // If a device memory is not in this mapping, it is not a temporary currently
117   // allocated and owned by this temporary memory manager.
118   std::map<DeviceMemoryBase, TemporaryMemoryRecord> records_
119       ABSL_GUARDED_BY(mutex_);
120 
121   // Allocation generation -- we bump this counter to distinguish temporary
122   // memory handles that have been deallocated and later reallocated at the same
123   // device memory address.
124   uint64_t generation_ ABSL_GUARDED_BY(mutex_);
125 
126   // The stream (parent object) for this temporary memory manager -- allocations
127   // are performed through this stream handle.
128   Stream* stream_;
129 
130   SE_DISALLOW_COPY_AND_ASSIGN(TemporaryMemoryManager);
131 };
132 
133 ////////////
134 // Inlines
135 
136 template <typename T>
137 port::StatusOr<std::unique_ptr<TemporaryDeviceMemory<T>>>
AllocateArray(uint64_t element_count)138 TemporaryMemoryManager::AllocateArray(uint64_t element_count) {
139   port::StatusOr<std::unique_ptr<TemporaryDeviceMemoryBase>> temporary_memory =
140       AllocateArrayBase(element_count, sizeof(T));
141   if (!temporary_memory.ok()) {
142     return temporary_memory.status();
143   }
144 
145   return std::unique_ptr<TemporaryDeviceMemory<T>>(
146       reinterpret_cast<TemporaryDeviceMemory<T>*>(temporary_memory->release()));
147 }
148 
149 }  // namespace internal
150 }  // namespace stream_executor
151 
152 #endif  // TENSORFLOW_COMPILER_XLA_STREAM_EXECUTOR_TEMPORARY_MEMORY_MANAGER_H_
153