Files
paddlepaddle--paddle/paddle/phi/api/include/compat/c10/cuda/CUDAGuard.h
T
2026-07-13 12:40:42 +08:00

170 lines
5.0 KiB
C++

// Copyright (c) 2025 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.
// The file has been adapted from pytorch project
// Licensed under BSD-style license -
// https://github.com/pytorch/pytorch/blob/main/LICENSE
#pragma once
#include <c10/core/Device.h>
#include <c10/util/Exception.h>
#include <optional>
#include "paddle/phi/backends/gpu/gpu_info.h"
namespace c10::cuda {
namespace detail {
inline Device current_cuda_device() {
return Device(kCUDA, phi::backends::gpu::GetCurrentDeviceId());
}
inline Device normalize_cuda_device(Device device) {
TORCH_CHECK(device.is_cuda(), "Expected a CUDA device, but got ", device);
return device.has_index() ? Device(kCUDA, device.index())
: current_cuda_device();
}
} // namespace detail
struct CUDAGuard {
explicit CUDAGuard() = delete; // NOLINT
explicit CUDAGuard(DeviceIndex device_index)
: original_device_(detail::current_cuda_device()),
current_device_(original_device_) {
set_index(device_index);
}
explicit CUDAGuard(Device device)
: original_device_(detail::current_cuda_device()),
current_device_(original_device_) {
set_device(device);
}
CUDAGuard(const CUDAGuard&) = delete;
CUDAGuard& operator=(const CUDAGuard&) = delete;
CUDAGuard(CUDAGuard&& other) = delete;
CUDAGuard& operator=(CUDAGuard&& other) = delete;
~CUDAGuard() {
// Always restore to original_device_ to handle cases where the device
// was changed outside of this guard, matching PyTorch semantics.
phi::backends::gpu::SetDeviceId(static_cast<int>(original_device_.index()));
}
void set_device(Device device) {
const Device normalized = detail::normalize_cuda_device(device);
if (normalized.index() != current_device_.index()) {
phi::backends::gpu::SetDeviceId(static_cast<int>(normalized.index()));
current_device_ = normalized;
}
}
void reset_device(Device device) { set_device(device); }
void set_index(DeviceIndex device_index) {
if (current_device_.index() != device_index) {
phi::backends::gpu::SetDeviceId(static_cast<int>(device_index));
current_device_ = Device(kCUDA, device_index);
}
}
Device original_device() const { return original_device_; }
Device current_device() const { return current_device_; }
private:
Device original_device_;
Device current_device_;
};
struct OptionalCUDAGuard {
OptionalCUDAGuard() = default;
explicit OptionalCUDAGuard(std::optional<Device> device_opt) {
if (device_opt.has_value()) {
set_device(device_opt.value());
}
}
explicit OptionalCUDAGuard(std::optional<DeviceIndex> device_index_opt) {
if (device_index_opt.has_value()) {
set_index(device_index_opt.value());
}
}
OptionalCUDAGuard(const OptionalCUDAGuard&) = delete;
OptionalCUDAGuard& operator=(const OptionalCUDAGuard&) = delete;
OptionalCUDAGuard(OptionalCUDAGuard&& other) = delete;
OptionalCUDAGuard& operator=(OptionalCUDAGuard&& other) = delete;
~OptionalCUDAGuard() { reset(); }
void set_device(Device device) {
const Device normalized = detail::normalize_cuda_device(device);
init_if_needed();
if (normalized.index() != current_device_->index()) {
phi::backends::gpu::SetDeviceId(static_cast<int>(normalized.index()));
}
current_device_ = normalized;
}
void reset_device(Device device) { set_device(device); }
void set_index(DeviceIndex device_index) {
init_if_needed();
if (device_index != current_device_->index()) {
phi::backends::gpu::SetDeviceId(static_cast<int>(device_index));
}
current_device_ = Device(kCUDA, device_index);
}
std::optional<Device> original_device() const { return original_device_; }
std::optional<Device> current_device() const { return current_device_; }
void reset() {
if (original_device_.has_value()) {
// Always restore to original_device_ to handle external device changes.
// This matches PyTorch OptionalDeviceGuard semantics.
phi::backends::gpu::SetDeviceId(
static_cast<int>(original_device_->index()));
}
original_device_.reset();
current_device_.reset();
}
private:
void init_if_needed() {
if (!original_device_.has_value()) {
original_device_ = detail::current_cuda_device();
current_device_ = original_device_;
}
}
std::optional<Device> original_device_;
std::optional<Device> current_device_;
};
} // namespace c10::cuda
namespace at::cuda {
using c10::cuda::CUDAGuard;
using c10::cuda::OptionalCUDAGuard;
} // namespace at::cuda