| 219 | } |
| 220 | |
| 221 | void StackPushOp::ComputeAsync(OpKernelContext* ctx, DoneCallback done) { |
| 222 | // Get the stack from the handle. |
| 223 | Stack* stack = nullptr; |
| 224 | OP_REQUIRES_OK_ASYNC(ctx, GetStack(ctx, &stack), done); |
| 225 | core::ScopedUnref unref(stack); |
| 226 | |
| 227 | if (ctx->input_dtype(1) != stack->ElemType()) { |
| 228 | ctx->CtxFailure(errors::InvalidArgument("Must have type ", |
| 229 | stack->ElemType(), " but got ", |
| 230 | ctx->input_dtype(1))); |
| 231 | done(); |
| 232 | return; |
| 233 | } |
| 234 | |
| 235 | // Push the tensor onto the stack. Swap the tensor to CPU if instructed. |
| 236 | const Tensor& tensor = ctx->input(1); |
| 237 | AllocatorAttributes alloc_attrs = ctx->input_alloc_attr(1); |
| 238 | // For now, we use a simple heuristic for swapping: A GPU tensor is moved |
| 239 | // to CPU if the tensor has more than kCopyThreshold bytes and the GPU |
| 240 | // allocator says more than kOccupancy of the memory is in use. |
| 241 | static constexpr int kCopyThreshold = 2048; |
| 242 | static constexpr double kOccupancy = 0.7; |
| 243 | if (swap_memory_ && !alloc_attrs.on_host() && |
| 244 | tensor.TotalBytes() > kCopyThreshold && stack->IsUsefulToSwap(tensor)) { |
| 245 | DeviceContext* device_ctxt = ctx->op_device_context(); |
| 246 | auto device = static_cast<tensorflow::Device*>(ctx->device()); |
| 247 | Allocator* allocator = device->GetAllocator(alloc_attrs); |
| 248 | absl::optional<AllocatorStats> stats = allocator->GetStats(); |
| 249 | if (stats && *stats->bytes_limit && |
| 250 | stats->bytes_in_use > (*stats->bytes_limit * kOccupancy)) { |
| 251 | // Asynchronously copy the tensor from GPU to CPU memory. |
| 252 | // TODO(yuanbyu): Swap the oldest tensor first. |
| 253 | AllocatorAttributes host_alloc_attrs; |
| 254 | host_alloc_attrs.set_gpu_compatible(true); |
| 255 | host_alloc_attrs.set_on_host(true); |
| 256 | Allocator* cpu_allocator = device->GetAllocator(host_alloc_attrs); |
| 257 | Tensor* cpu_tensor = |
| 258 | new Tensor(cpu_allocator, tensor.dtype(), tensor.shape()); |
| 259 | device_ctxt->CopyDeviceTensorToCPU( |
| 260 | &tensor, "StackPush", device, cpu_tensor, |
| 261 | [cpu_tensor, stack, ctx, done](const Status& s) { |
| 262 | ctx->SetStatus(s); |
| 263 | if (s.ok()) { |
| 264 | AllocatorAttributes alloc_attrs = ctx->input_alloc_attr(1); |
| 265 | ctx->SetStatus(stack->Push({*cpu_tensor, alloc_attrs, true})); |
| 266 | } |
| 267 | if (ctx->status().ok()) { |
| 268 | ctx->set_output(0, *cpu_tensor); |
| 269 | } |
| 270 | done(); |
| 271 | delete cpu_tensor; |
| 272 | }); |
| 273 | return; |
| 274 | } |
| 275 | } |
| 276 | |
| 277 | // Execute synchronously if not swapped. |
| 278 | OP_REQUIRES_OK_ASYNC(ctx, stack->Push({tensor, alloc_attrs, false}), done); |
nothing calls this directly
no test coverage detected