Files
2026-07-13 12:40:42 +08:00

336 lines
12 KiB
C++

// Copyright (c) 2022 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 "paddle/fluid/pybind/xpu_streams_py.h"
#include <string>
#include <vector>
#include "paddle/phi/api/profiler/event.h"
#include "paddle/phi/core/platform/device_event_base.h"
#if defined(PADDLE_WITH_XPU)
#include <cuda.h>
#include <cuda_runtime.h>
#include "paddle/phi/backends/xpu/enforce_xpu.h"
#include "xpu/runtime.h"
#include "xpu/runtime_ex.h"
#endif
namespace py = pybind11;
namespace paddle {
namespace platform {
#ifdef PADDLE_WITH_XPU
phi::XPUStreamHandle *get_current_stream(int device_id) {
auto place = phi::XPUPlace(device_id);
auto *dev_ctx = static_cast<phi::XPUContext *>(
phi::DeviceContextPool::Instance().Get(place));
dev_ctx->Wait();
return dev_ctx->get_current_stream_handle();
}
phi::XPUStreamHandle *set_current_stream(int idx) {
int device_id = phi::backends::xpu::GetXPUCurrentDeviceId();
auto original_stream = get_current_stream(device_id);
auto place = phi::XPUPlace(device_id);
auto *dev_ctx = static_cast<phi::XPUContext *>(
phi::DeviceContextPool::Instance().Get(place));
dev_ctx->SetCurrentStream(idx);
return original_stream;
}
#endif
} // namespace platform
namespace pybind {
void BindXpuStream(py::module *m_ptr) {
auto &m = *m_ptr;
// Bind Methods
m.def("_xpu_device_synchronize", [](int device_id) {
#ifdef PADDLE_WITH_XPU
if (device_id == -1) {
device_id = paddle::platform::GetXPUCurrentDeviceId();
}
int curr_device_id = paddle::platform::GetXPUCurrentDeviceId();
paddle::platform::SetXPUDeviceId(device_id);
auto place = phi::XPUPlace(device_id);
auto *dev_ctx = static_cast<phi::XPUContext *>(
phi::DeviceContextPool::Instance().Get(place));
dev_ctx->Wait();
paddle::platform::SetXPUDeviceId(curr_device_id);
#else
PADDLE_THROW(common::errors::Unavailable(
"Paddle is not compiled with XPU. Cannot visit device synchronize."));
#endif
});
m.def(
"_xpu_get_current_stream",
[](int device_id) {
#ifdef PADDLE_WITH_XPU
if (device_id == -1) {
device_id = paddle::platform::GetXPUCurrentDeviceId();
}
paddle::platform::SetXPUDeviceId(device_id);
return platform::get_current_stream(device_id);
#else
PADDLE_THROW(
common::errors::Unavailable("Paddle is not compiled with XPU. "
"Cannot visit device synchronize."));
#endif
},
py::return_value_policy::reference);
m.def(
"_xpu_set_current_stream",
[](int stream_id) {
#ifdef PADDLE_WITH_XPU
return platform::set_current_stream(stream_id);
#else
PADDLE_THROW(
common::errors::Unavailable("Paddle is not compiled with XPU. "
"Cannot visit device synchronize."));
#endif
},
py::return_value_policy::reference);
m.def("_device_synchronize", [](int device_id) {
#ifdef PADDLE_WITH_XPU
if (device_id == -1) {
device_id = paddle::platform::GetXPUCurrentDeviceId();
}
int curr_device_id = paddle::platform::GetXPUCurrentDeviceId();
paddle::platform::SetXPUDeviceId(device_id);
PADDLE_ENFORCE_XPU_SUCCESS(cudaDeviceSynchronize());
paddle::platform::SetXPUDeviceId(curr_device_id);
#else
PADDLE_THROW(common::errors::Unavailable(
"Paddle is not compiled with CUDA. Cannot visit device synchronize."));
#endif
});
py::class_<phi::XPUStreamHandle>(m, "XPUStream", R"DOC(
The handle of the XPU stream.
Parameters:
device(paddle.XPUPlace()|int|None, optional): The device which wanted to allocate the stream.
If device is None or negative integer, device will be the current device.
If device is positive integer, it must less than the device count. Default: None.
Examples:
.. code-block:: pycon
>>> # doctest: +REQUIRES(env:XPU)
>>> import paddle
>>> s1 = paddle.device.xpu.Stream(paddle.XPUPlace(0))
>>> s2 = paddle.device.xpu.Stream(0)
>>> s3 = paddle.device.xpu.Stream()
)DOC")
#ifdef PADDLE_WITH_XPU
.def_property_readonly(
"xpu_stream",
[](phi::XPUStreamHandle &self) {
return reinterpret_cast<std::uintptr_t>(self.raw_stream());
})
.def("wait_stream",
[](phi::XPUStreamHandle &self, phi::XPUStreamHandle &other) {
auto *dev_ctx = phi::get_xpu_context();
dev_ctx->StreamWaitStreamInPool(self.id(), other.id());
})
.def("wait_event",
[](phi::XPUStreamHandle &self, phi::XPUEventHandle &other) {
self.wait_event(other.get_event());
})
.def("query",
[](phi::XPUStreamHandle &self) {
PADDLE_THROW(common::errors::Unavailable(
"Query function for XPUStream is not supported now"));
})
.def("record_event",
[](phi::XPUStreamHandle &self, phi::XPUEventHandle *event) {
if (event == nullptr) {
event = new phi::XPUEventHandle();
}
self.record_event(event->get_event());
return event;
})
.def(
"synchronize",
[](phi::XPUStreamHandle &self) { self.synchronize(); },
R"DOC(
Waits for stream tasks to complete.
Examples:
.. code-block:: pycon
>>> # doctest: +REQUIRES(env:XPU)
>>> import paddle
>>> s = paddle.device.xpu.Stream(paddle.XPUPlace(0), 1)
>>> s.synchronize()
)DOC")
.def_property_readonly(
"place",
[](phi::XPUStreamHandle &self) {
return phi::XPUPlace(platform::GetXPUCurrentDeviceId());
})
.def_property_readonly(
"idx", [](phi::XPUStreamHandle &self) { return self.id(); })
#endif
.def("__init__",
[](phi::XPUStreamHandle &self) {
#ifdef PADDLE_WITH_XPU
new (&self) phi::XPUStreamHandle();
self.Init();
#else
PADDLE_THROW(common::errors::Unavailable(
"Class XPUStream can only be initialized on the XPU "
"platform."));
#endif
})
.def(
"__init__",
[](phi::XPUStreamHandle &self, phi::XPUPlace *place) {
#ifdef PADDLE_WITH_XPU
if (place == nullptr) {
int curr_device_id = platform::GetXPUCurrentDeviceId();
auto place_tmp = phi::XPUPlace(curr_device_id);
new (&self) phi::XPUStreamHandle(place_tmp);
} else {
new (&self) phi::XPUStreamHandle(*place);
}
#else
PADDLE_THROW(common::errors::Unavailable(
"Class XPUStream can only be initialized on the XPU "
"platform."));
#endif
},
py::arg("device") = nullptr)
.def(
"__init__",
[](phi::XPUStreamHandle &self, int device) {
#ifdef PADDLE_WITH_XPU
if (device < 0) {
device = platform::GetXPUCurrentDeviceId();
}
auto place_tmp = phi::XPUPlace(device);
new (&self) phi::XPUStreamHandle(place_tmp);
#else
PADDLE_THROW(common::errors::Unavailable(
"Class XPUStream can only be initialized on the XPU "
"platform."));
#endif
},
py::arg("device") = -1);
py::class_<phi::XPUEventHandle>(m, "XPUEvent", R"DOC(
The handle of the XPU event.
Examples:
.. code-block:: pycon
>>> # doctest: +REQUIRES(env:XPU)
>>> import paddle
>>> event = paddle.device.xpu.Event()
)DOC")
#ifdef PADDLE_WITH_XPU
.def(
"record",
[](phi::XPUEventHandle &self, phi::XPUStreamHandle *stream) {
if (stream == nullptr) {
auto *dev_ctx = phi::get_xpu_context();
auto stream_handle = dev_ctx->get_current_stream_handle();
self.record(stream_handle->raw_stream());
} else {
self.record(stream->raw_stream());
}
},
py::arg("stream") = nullptr)
.def("query", [](phi::XPUEventHandle &self) { return self.query(); })
.def("elapsed_time",
[](phi::XPUEventHandle &self) {
PADDLE_THROW(common::errors::Unavailable(
"XPUEvent elapsed_time is not supported now"));
})
.def("synchronize", [](phi::XPUEventHandle &self) { self.synchronize(); })
#endif
.def("__init__", [](phi::XPUEventHandle &self) {
#ifdef PADDLE_WITH_XPU
new (&self) phi::XPUEventHandle();
#else
PADDLE_THROW(common::errors::Unavailable(
"Class XPUEvent can only be initialized on the XPU platform."));
#endif
});
#ifdef PADDLE_WITH_XPU
py::class_<phi::XPUCUDAStream>(m, "XPUCUDAStream", R"DOC(
The handle of the XPU stream.
Parameters:
device(paddle.XPUPlace()|int|None, optional): The device which wanted to allocate the stream.
If device is None or negative integer, device will be the current device.
If device is positive integer, it must less than the device count. Default: None.
priority(int|None, optional): The priority of stream. The priority can be 1(high) or 2(normal).
If priority is None, the priority is 2(normal). Default: None.
Examples:
.. code-block:: pycon
>>> # doctest: +REQUIRES(env:XPU)
>>> import paddle
>>> s1 = paddle.device.xpu.Stream(paddle.XPUPlace(0), 1)
>>> s2 = paddle.device.xpu.Stream(0, 1)
>>> s3 = paddle.device.xpu.Stream()
)DOC")
.def(
"synchronize",
[](phi::XPUCUDAStream &self) { self.Synchronize(); },
R"DOC(
Waits for stream tasks to complete.
Examples:
.. code-block:: pycon
>>> # doctest: +REQUIRES(env:GPU)
>>> import paddle
>>> s = paddle.device.cuda.Stream(paddle.CUDAPlace(0), 1)
>>> s.synchronize()
)DOC")
.def("__init__",
[](phi::XPUCUDAStream &self, phi::XPUPlace *place, int priority) {
if (priority != 1 && priority != 2) {
PADDLE_THROW(common::errors::InvalidArgument(
"Priority should be 1(high) or 2(normal) "));
}
auto stream_flag =
phi::XPUCUDAStream::StreamFlag::kStreamNonBlocking;
if (place == nullptr) {
int curr_device_id = platform::GetXPUCurrentDeviceId();
auto place_tmp = phi::XPUPlace(curr_device_id);
new (&self)
phi::XPUCUDAStream(place_tmp, priority - 2, stream_flag);
} else {
new (&self)
phi::XPUCUDAStream(*place, priority - 2, stream_flag);
}
});
#endif
}
} // namespace pybind
} // namespace paddle