// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) #include "paddle/phi/core/platform/cuda_device_guard.h" #endif #include "paddle/common/flags.h" #include "paddle/fluid/framework/garbage_collector.h" #include "paddle/phi/core/platform/device/device_wrapper.h" #include "glog/logging.h" COMMON_DECLARE_double(eager_delete_tensor_gb); COMMON_DECLARE_double(memory_fraction_of_eager_deletion); COMMON_DECLARE_bool(fast_eager_deletion_mode); namespace paddle::framework { GarbageCollector::GarbageCollector(const Place &place, size_t max_memory_size) : garbages_(std::make_unique()), mutex_(nullptr), max_memory_size_((std::max)(max_memory_size, static_cast(1))) { dev_ctx_ = phi::DeviceContextPool::Instance().Get(place); if (max_memory_size_ > 1) { mutex_ = std::make_unique(); } } CPUGarbageCollector::CPUGarbageCollector(const CPUPlace &place, size_t max_memory_size) : GarbageCollector(place, max_memory_size) {} void CPUGarbageCollector::ClearCallback(const std::function &callback) { callback(); } #ifdef PADDLE_WITH_XPU XPUGarbageCollector::XPUGarbageCollector(const XPUPlace &place, size_t max_memory_size) : GarbageCollector(place, max_memory_size) {} void XPUGarbageCollector::ClearCallback(const std::function &callback) { callback(); } XPUPinnedGarbageCollector::XPUPinnedGarbageCollector( const phi::XPUPinnedPlace &place, size_t max_memory_size) : GarbageCollector(place, max_memory_size) {} void XPUPinnedGarbageCollector::ClearCallback( const std::function &callback) { callback(); } #endif #ifdef PADDLE_WITH_IPU IPUGarbageCollector::IPUGarbageCollector(const phi::IPUPlace &place, size_t max_memory_size) : GarbageCollector(place, max_memory_size) {} void IPUGarbageCollector::ClearCallback(const std::function &callback) { callback(); } #endif #if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) UnsafeFastGPUGarbageCollector::UnsafeFastGPUGarbageCollector( const GPUPlace &place, size_t max_memory_size) : GarbageCollector(place, max_memory_size) {} void UnsafeFastGPUGarbageCollector::ClearCallback( const std::function &callback) { callback(); } DefaultStreamGarbageCollector::DefaultStreamGarbageCollector( const GPUPlace &place, size_t max_memory_size) : GarbageCollector(place, max_memory_size) {} void DefaultStreamGarbageCollector::Wait() const { static_cast(this->dev_ctx_)->WaitStreamCallback(); } void DefaultStreamGarbageCollector::ClearCallback( const std::function &callback) { static_cast(this->dev_ctx_)->AddStreamCallback(callback); } StreamGarbageCollector::StreamGarbageCollector(const GPUPlace &place, size_t max_memory_size) : GarbageCollector(place, max_memory_size), stream_(nullptr), callback_manager_(nullptr) { platform::CUDADeviceGuard guard(place.device); #ifdef PADDLE_WITH_HIP PADDLE_ENFORCE_GPU_SUCCESS(hipStreamCreate(&stream_)); #else PADDLE_ENFORCE_GPU_SUCCESS(cudaStreamCreate(&stream_)); callback_manager_ = std::make_unique>(stream_); #endif } StreamGarbageCollector::~StreamGarbageCollector() { auto place = this->dev_ctx_->GetPlace(); platform::CUDADeviceGuard guard(place.device); platform::GpuStreamSync(stream_); platform::GpuDestroyStream(stream_); } gpuStream_t StreamGarbageCollector::stream() const { return stream_; } void StreamGarbageCollector::Wait() const { callback_manager_->Wait(); } void StreamGarbageCollector::ClearCallback( const std::function &callback) { callback_manager_->AddCallback(callback); } CUDAPinnedGarbageCollector::CUDAPinnedGarbageCollector( const GPUPinnedPlace &place, size_t max_memory_size) : GarbageCollector(place, max_memory_size) {} void CUDAPinnedGarbageCollector::ClearCallback( const std::function &callback) { callback(); } #endif #ifdef PADDLE_WITH_CUSTOM_DEVICE CustomDefaultStreamGarbageCollector::CustomDefaultStreamGarbageCollector( const phi::CustomPlace &place, size_t max_memory_size) : GarbageCollector(place, max_memory_size) {} void CustomDefaultStreamGarbageCollector::Wait() const { static_cast(this->dev_ctx_)->WaitStreamCallback(); } void CustomDefaultStreamGarbageCollector::ClearCallback( const std::function &callback) { static_cast(this->dev_ctx_) ->AddStreamCallback(callback); } CustomDeviceUnsafeFastGarbageCollector::CustomDeviceUnsafeFastGarbageCollector( const phi::CustomPlace &place, size_t max_memory_size) : GarbageCollector(place, max_memory_size) {} void CustomDeviceUnsafeFastGarbageCollector::ClearCallback( const std::function &callback) { callback(); } CustomStreamGarbageCollector::CustomStreamGarbageCollector( const phi::CustomPlace &place, size_t max_memory_size) : GarbageCollector(place, max_memory_size) { phi::DeviceGuard guard(place); stream_ = std::make_unique(); stream_->Init(place); callback_manager_ = std::make_unique(stream_.get()); } CustomStreamGarbageCollector::~CustomStreamGarbageCollector() { phi::DeviceGuard guard(this->dev_ctx_->GetPlace()); stream_->Synchronize(); stream_->Destroy(); } phi::stream::Stream *CustomStreamGarbageCollector::stream() const { return stream_.get(); } void CustomStreamGarbageCollector::Wait() const { callback_manager_->Wait(); } void CustomStreamGarbageCollector::ClearCallback( const std::function &callback) { callback_manager_->AddCallback(callback); } #endif int64_t GetEagerDeletionThreshold() { return FLAGS_eager_delete_tensor_gb < 0 ? -1 : static_cast(FLAGS_eager_delete_tensor_gb * (static_cast(1) << 30)); } bool IsFastEagerDeletionModeEnabled() { return FLAGS_fast_eager_deletion_mode; } void SetEagerDeletionMode(double threshold, double fraction, bool fast_mode) { FLAGS_eager_delete_tensor_gb = threshold; FLAGS_memory_fraction_of_eager_deletion = fraction; FLAGS_fast_eager_deletion_mode = fast_mode; } double GetEagerDeletionMemoryFraction() { return FLAGS_memory_fraction_of_eager_deletion; } std::unique_ptr CreateGarbageCollector( const Place &place, const size_t max_memory_size) { std::unique_ptr gc = nullptr; if (phi::is_gpu_place(place)) { #if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) if (IsFastEagerDeletionModeEnabled()) { gc = std::make_unique(place, max_memory_size); } else { gc = std::make_unique(place, max_memory_size); } #else PADDLE_THROW( common::errors::Unimplemented("No GPU gc found in CPU/XPU paddle")); #endif } else if (phi::is_cpu_place(place)) { gc = std::make_unique(place, max_memory_size); } else if (phi::is_xpu_place(place)) { #ifdef PADDLE_WITH_XPU gc = std::make_unique(place, max_memory_size); #else PADDLE_THROW( common::errors::Unimplemented("No XPU gc found in CPU/GPU paddle")); #endif } else if (phi::is_ipu_place(place)) { #ifdef PADDLE_WITH_IPU gc = std::make_unique(place, max_memory_size); #else PADDLE_THROW( common::errors::Unimplemented("No IPU gc found in CPU/IPU paddle")); #endif } else if (phi::is_custom_place(place)) { #ifdef PADDLE_WITH_CUSTOM_DEVICE if (IsFastEagerDeletionModeEnabled()) { VLOG(4) << "Use unsafe fast gc for " << place << "."; gc = std::make_unique( place, max_memory_size); } else { VLOG(4) << "Use default stream gc for " << place << "."; gc = std::make_unique( place, max_memory_size); } #else PADDLE_THROW(common::errors::Unimplemented("No CustomDevice gc found")); #endif } return std::unique_ptr(gc.release()); } } // namespace paddle::framework