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(&registered_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