1 /* Copyright 2017 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 #include "tensorflow/compiler/xla/service/gpu/buffer_allocations.h"
17
18 #include <utility>
19
20 #include "absl/memory/memory.h"
21 #include "tensorflow/compiler/xla/map_util.h"
22 #include "tensorflow/compiler/xla/service/gpu/gpu_constants.h"
23 #include "tensorflow/compiler/xla/status_macros.h"
24 #include "tensorflow/compiler/xla/types.h"
25 #include "tensorflow/compiler/xla/util.h"
26 #include "tensorflow/core/lib/core/errors.h"
27 #include "tensorflow/core/lib/gtl/map_util.h"
28 #include "tensorflow/core/lib/strings/numbers.h"
29 #include "tensorflow/core/platform/logging.h"
30 #include "tensorflow/core/platform/types.h"
31
32 namespace xla {
33 namespace gpu {
34
RegisterBuffer(BufferAllocation::Index index,se::DeviceMemoryBase address)35 void BufferAllocations::Builder::RegisterBuffer(BufferAllocation::Index index,
36 se::DeviceMemoryBase address) {
37 InsertOrDie(®istered_buffers_, index, address);
38 }
39
Build(const BufferAssignment * buffer_assignment,int device_ordinal,DeviceMemoryAllocator * memory_allocator)40 StatusOr<std::unique_ptr<BufferAllocations>> BufferAllocations::Builder::Build(
41 const BufferAssignment* buffer_assignment, int device_ordinal,
42 DeviceMemoryAllocator* memory_allocator) {
43 const int64 num_buffers = buffer_assignment->Allocations().size();
44 auto buffer_allocations = absl::WrapUnique(new BufferAllocations(
45 num_buffers, device_ordinal, memory_allocator, buffer_assignment));
46
47 for (BufferAllocation::Index i = 0; i < num_buffers; ++i) {
48 const BufferAllocation& allocation = buffer_assignment->GetAllocation(i);
49 const int64 expected_alignment = [&] {
50 if (allocation.is_entry_computation_parameter()) {
51 return kEntryParameterAlignBytes;
52 } else if (allocation.is_constant()) {
53 return kConstantBufferAlignBytes;
54 } else {
55 return kXlaAllocatedBufferAlignBytes;
56 }
57 }();
58
59 // If buffer #i's address is already registered (e.g. external arguments or
60 // result buffers), use that registered buffer.
61 if (se::DeviceMemoryBase* address =
62 tensorflow::gtl::FindOrNull(registered_buffers_, i)) {
63 if (reinterpret_cast<uintptr_t>(address->opaque()) % expected_alignment !=
64 0) {
65 return InternalError(
66 "Address of registered buffer %d must be a multiple of %x, but "
67 "was %p",
68 i, kEntryParameterAlignBytes, address->opaque());
69 }
70 buffer_allocations->SetBuffer(i, *address);
71 continue;
72 }
73
74 // Allocate each allocation that might escape, or is the temp buffer.
75 bool seen_temp_buffer = false;
76 if (allocation.maybe_live_out() || allocation.IsPreallocatedTempBuffer()) {
77 const int64 buffer_size = allocation.size();
78 se::DeviceMemoryBase buffer_address;
79 if (buffer_size > 0) {
80 OwningDeviceMemory buffer;
81 TF_ASSIGN_OR_RETURN(
82 buffer, memory_allocator->Allocate(device_ordinal, buffer_size));
83 if (reinterpret_cast<uintptr_t>(buffer.opaque()) % expected_alignment !=
84 0) {
85 return InternalError(
86 "Address returned by memory_allocator->Allocate must be a "
87 "multiple of 0x%x, but was %p",
88 kXlaAllocatedBufferAlignBytes, buffer.opaque());
89 }
90 // We do manual memory management within BufferAllocations. Be sure not
91 // to do a TF_RETURN_IF_ERROR between this line and the
92 // buffer_allocations->SetBuffer(buffer_address) call below!
93 buffer_address = buffer.Forget();
94 }
95
96 buffer_allocations->SetBuffer(i, buffer_address);
97 if (allocation.IsPreallocatedTempBuffer()) {
98 if (seen_temp_buffer) {
99 LOG(FATAL) << "Multiple temporary buffers detected. BufferAssigner "
100 << "must guarantee at most one temporary buffer.";
101 }
102 seen_temp_buffer = true;
103 buffer_allocations->temp_buffer_base_ = buffer_address;
104 }
105 }
106 }
107
108 if (VLOG_IS_ON(2)) {
109 for (BufferAllocation::Index i = 0; i < num_buffers; ++i) {
110 const auto& buf = buffer_allocations->buffers_[i];
111 VLOG(2) << "Buffer " << i << " -> " << buf.opaque() << " (" << buf.size()
112 << "B)";
113 }
114 }
115 return std::move(buffer_allocations);
116 }
117
~BufferAllocations()118 BufferAllocations::~BufferAllocations() {
119 if (!torn_down_) {
120 // Presumably if we're executing this branch, the caller is in an error
121 // state, otherwise it would have explicitly called TearDown so it could
122 // save some set of live addresses. So ignoring any errors in TearDown is
123 // sensible.
124 TearDown(/*live_addresses=*/{}).IgnoreError();
125 }
126 }
127
TearDown(const std::set<se::DeviceMemoryBase> & live_addresses)128 Status BufferAllocations::TearDown(
129 const std::set<se::DeviceMemoryBase>& live_addresses) {
130 // Deallocate temporary buffers, taking care to try to deallocate all of them
131 // even if one of the deallocations fails.
132 Status status;
133 const int64 num_buffers = buffer_assignment_->Allocations().size();
134 for (BufferAllocation::Index i = 0; i < num_buffers; ++i) {
135 const BufferAllocation& allocation = buffer_assignment_->GetAllocation(i);
136 se::DeviceMemoryBase buffer_address = GetDeviceAddress(allocation.index());
137 // Deallocate buffers marked "maybe_live_out" but aren't actually live out,
138 // and temp buffers.
139 if ((allocation.maybe_live_out() &&
140 !live_addresses.count(buffer_address)) ||
141 allocation.IsPreallocatedTempBuffer()) {
142 auto dealloc_result =
143 memory_allocator_->Deallocate(device_ordinal_, buffer_address);
144 if (!dealloc_result.ok() && status.ok()) {
145 status = dealloc_result;
146 }
147 }
148 }
149 torn_down_ = true;
150 return status;
151 }
152
GetDeviceAddress(BufferAllocation::Index buffer_index) const153 se::DeviceMemoryBase BufferAllocations::GetDeviceAddress(
154 BufferAllocation::Index buffer_index) const {
155 CHECK_GE(buffer_index, 0);
156 CHECK_LT(buffer_index, buffers_.size());
157 return buffers_[buffer_index];
158 }
159
GetDeviceAddress(const BufferAllocation::Slice & buffer_slice) const160 se::DeviceMemoryBase BufferAllocations::GetDeviceAddress(
161 const BufferAllocation::Slice& buffer_slice) const {
162 se::DeviceMemoryBase base = GetDeviceAddress(buffer_slice.index());
163 CHECK_LE(buffer_slice.offset(), base.size());
164 CHECK_LE(buffer_slice.offset() + buffer_slice.size(), base.size());
165 return se::DeviceMemoryBase(
166 static_cast<char*>(base.opaque()) + buffer_slice.offset(),
167 buffer_slice.size(), /*is_sub_buffer=*/true);
168 }
169
SetBuffer(BufferAllocation::Index buffer_index,se::DeviceMemoryBase buffer)170 void BufferAllocations::SetBuffer(BufferAllocation::Index buffer_index,
171 se::DeviceMemoryBase buffer) {
172 CHECK_GE(buffer_index, 0);
173 CHECK_LT(buffer_index, buffers_.size());
174 buffers_[buffer_index] = buffer;
175 }
176
ShouldEmitLiteralInLlvmIr(const Literal & literal)177 bool ShouldEmitLiteralInLlvmIr(const Literal& literal) {
178 // LLVM can sometimes do interesting optimizations using scalar constants.
179 return ShapeUtil::IsScalar(literal.shape());
180 }
181
182 } // namespace gpu
183 } // namespace xla
184