// 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. #if defined(PADDLE_WITH_CUDA) #include "paddle/fluid/pybind/cudart_py.h" #include #include #include #include #include "paddle/phi/core/platform/cuda_device_guard.h" #if !defined(USE_ROCM) #include #else #include #endif namespace py = pybind11; namespace paddle { namespace pybind { void BindCudaRt(py::module* m) { auto cudart = m->def_submodule("_cudart", "libcudart.so bindings"); struct PaddleCudaError { cudaError_t value; PaddleCudaError() : value(cudaSuccess) {} explicit PaddleCudaError(cudaError_t v) : value(v) {} explicit PaddleCudaError(int v) : value(static_cast(v)) {} operator cudaError_t() const { return value; } operator int() const { return static_cast(value); } bool operator==(const PaddleCudaError& other) const { return value == other.value; } bool operator!=(const PaddleCudaError& other) const { return value != other.value; } bool operator==(cudaError_t other) const { return value == other; } bool operator!=(cudaError_t other) const { return value != other; } bool operator==(int other) const { return static_cast(value) == other; } bool operator!=(int other) const { return static_cast(value) != other; } int to_int() const { return static_cast(value); } cudaError_t get_value() const { return value; } }; py::class_(cudart, "cudaError") .def(py::init(), "Create from integer value") .def(py::init<>(), "Default constructor") .def("__int__", &PaddleCudaError::to_int) .def("get_value", &PaddleCudaError::get_value, "Get the underlying cudaError_t value") .def("__eq__", [](const PaddleCudaError& a, const PaddleCudaError& b) { return a == b; }) .def("__eq__", [](const PaddleCudaError& a, int b) { return a == b; }) .def("__ne__", [](const PaddleCudaError& a, const PaddleCudaError& b) { return a != b; }) .def("__ne__", [](const PaddleCudaError& a, int b) { return a != b; }) .def("__repr__", [](const PaddleCudaError& error) -> std::string { switch (error.value) { case cudaSuccess: return "cudaError.success"; default: return "cudaError(" + std::to_string(static_cast(error.value)) + ")"; } }); cudart.attr("cudaError").attr("success") = PaddleCudaError(cudaSuccess); cudart.def( "cudaGetErrorString", [](const PaddleCudaError& error) -> std::string { return std::string(cudaGetErrorString(error.value)); }, "Get error string for cuda error"); cudart.def( "cudaGetErrorString", [](int error_code) -> std::string { return std::string( cudaGetErrorString(static_cast(error_code))); }, "Get error string for cuda error code"); cudart.def("cudaGetErrorString", cudaGetErrorString); cudart.def("cudaProfilerStart", #ifdef USE_ROCM []() -> PaddleCudaError { return PaddleCudaError(hipSuccess); } #else []() -> PaddleCudaError { py::gil_scoped_release no_gil; return PaddleCudaError(cudaProfilerStart()); } #endif ); cudart.def("cudaProfilerStop", #ifdef USE_ROCM []() -> PaddleCudaError { return PaddleCudaError(hipSuccess); } #else []() -> PaddleCudaError { py::gil_scoped_release no_gil; return PaddleCudaError(cudaProfilerStop()); } #endif ); cudart.def( "cudaHostRegister", [](uintptr_t ptr, size_t size, unsigned int flags) -> PaddleCudaError { py::gil_scoped_release no_gil; cudaError_t result = cudaHostRegister(reinterpret_cast(ptr), size, flags); return PaddleCudaError(result); }); cudart.def("cudaHostUnregister", [](uintptr_t ptr) -> PaddleCudaError { py::gil_scoped_release no_gil; cudaError_t result = cudaHostUnregister(reinterpret_cast(ptr)); return PaddleCudaError(result); }); cudart.def("cudaStreamCreate", [](uintptr_t ptr) -> PaddleCudaError { py::gil_scoped_release no_gil; cudaError_t result = cudaStreamCreate(reinterpret_cast(ptr)); return PaddleCudaError(result); }); cudart.def("cudaStreamDestroy", [](uintptr_t ptr) -> PaddleCudaError { py::gil_scoped_release no_gil; cudaError_t result = cudaStreamDestroy(reinterpret_cast(ptr)); return PaddleCudaError(result); }); #if !defined(USE_ROCM) && defined(CUDA_VERSION) && CUDA_VERSION < 12000 // cudaProfilerInitialize is no longer needed after CUDA 12 cudart.def("cudaProfilerInitialize", [](const char* configFile, const char* outputFile, cudaOutputMode_t outputMode) -> PaddleCudaError { py::gil_scoped_release no_gil; cudaError_t result = cudaProfilerInitialize(configFile, outputFile, outputMode); return PaddleCudaError(result); }); #endif cudart.def("cudaMemGetInfo", [](int device) -> std::pair { const auto& place = GPUPlace(device); platform::CUDADeviceGuard cuda_guard(place); size_t device_free = 0; size_t device_total = 0; py::gil_scoped_release no_gil; cudaMemGetInfo(&device_free, &device_total); return {device_free, device_total}; }); cudart.def( "cudaMemcpy", [](py::int_ dst, py::int_ src, size_t count, int kind) -> PaddleCudaError { void* dst_ptr = reinterpret_cast(static_cast(dst)); const void* src_ptr = reinterpret_cast(static_cast(src)); cudaError_t result = cudaMemcpy( dst_ptr, src_ptr, count, static_cast(kind)); return PaddleCudaError(result); }, "Copy memory"); cudart.def( "cudaMemcpyAsync", [](py::int_ dst, py::int_ src, size_t count, int kind, py::int_ stream) -> PaddleCudaError { void* dst_ptr = reinterpret_cast(static_cast(dst)); const void* src_ptr = reinterpret_cast(static_cast(src)); cudaStream_t cuda_stream = reinterpret_cast(static_cast(stream)); cudaError_t result = cudaMemcpyAsync(dst_ptr, src_ptr, count, static_cast(kind), cuda_stream); return PaddleCudaError(result); }, "Copy memory asynchronously"); cudart.def( "cudaStreamSynchronize", [](py::int_ stream) -> PaddleCudaError { cudaStream_t cuda_stream = reinterpret_cast(static_cast(stream)); cudaError_t result = cudaStreamSynchronize(cuda_stream); return PaddleCudaError(result); }, "Synchronize stream"); cudart.def( "cudaDeviceSynchronize", []() -> PaddleCudaError { cudaError_t result = cudaDeviceSynchronize(); return PaddleCudaError(result); }, "Synchronize device"); cudart.def( "cudaGetLastError", []() -> PaddleCudaError { cudaError_t result = cudaGetLastError(); return PaddleCudaError(result); }, "Get last CUDA error"); cudart.def( "cudaPeekAtLastError", []() -> PaddleCudaError { cudaError_t result = cudaPeekAtLastError(); return PaddleCudaError(result); }, "Peek at last CUDA error without clearing it"); cudart.attr("cudaMemcpyHostToHost") = static_cast(cudaMemcpyHostToHost); cudart.attr("cudaMemcpyHostToDevice") = static_cast(cudaMemcpyHostToDevice); cudart.attr("cudaMemcpyDeviceToHost") = static_cast(cudaMemcpyDeviceToHost); cudart.attr("cudaMemcpyDeviceToDevice") = static_cast(cudaMemcpyDeviceToDevice); cudart.attr("cudaMemcpyDefault") = static_cast(cudaMemcpyDefault); cudart.attr("cudaHostRegisterDefault") = static_cast(cudaHostRegisterDefault); cudart.attr("cudaHostRegisterPortable") = static_cast(cudaHostRegisterPortable); cudart.attr("cudaHostRegisterMapped") = static_cast(cudaHostRegisterMapped); cudart.attr("cudaHostRegisterIoMemory") = static_cast(cudaHostRegisterIoMemory); #if !defined(USE_ROCM) && defined(CUDA_VERSION) && CUDA_VERSION < 12000 struct PaddleCudaOutputMode { cudaOutputMode_t value; PaddleCudaOutputMode() : value(cudaKeyValuePair) {} explicit PaddleCudaOutputMode(cudaOutputMode_t v) : value(v) {} explicit PaddleCudaOutputMode(int v) : value(static_cast(v)) {} operator cudaOutputMode_t() const { return value; } operator int() const { return static_cast(value); } bool operator==(const PaddleCudaOutputMode& other) const { return value == other.value; } bool operator!=(const PaddleCudaOutputMode& other) const { return value != other.value; } bool operator==(cudaOutputMode_t other) const { return value == other; } bool operator!=(cudaOutputMode_t other) const { return value != other; } bool operator==(int other) const { return static_cast(value) == other; } bool operator!=(int other) const { return static_cast(value) != other; } int to_int() const { return static_cast(value); } }; py::class_(cudart, "cudaOutputMode") .def(py::init(), "Create from integer value") .def("__int__", &PaddleCudaOutputMode::to_int) .def("__eq__", [](const PaddleCudaOutputMode& a, const PaddleCudaOutputMode& b) { return a == b; }) .def("__eq__", [](const PaddleCudaOutputMode& a, int b) { return a == b; }) .def("__ne__", [](const PaddleCudaOutputMode& a, const PaddleCudaOutputMode& b) { return a != b; }) .def("__ne__", [](const PaddleCudaOutputMode& a, int b) { return a != b; }) .def("__repr__", [](const PaddleCudaOutputMode& mode) -> std::string { switch (mode.value) { case cudaKeyValuePair: return "cudaOutputMode.KeyValuePair"; case cudaCSV: return "cudaOutputMode.CSV"; default: return "cudaOutputMode(" + std::to_string(static_cast(mode.value)) + ")"; } }); cudart.attr("cudaOutputMode").attr("KeyValuePair") = PaddleCudaOutputMode(cudaKeyValuePair); cudart.attr("cudaOutputMode").attr("CSV") = PaddleCudaOutputMode(cudaCSV); #endif cudart.def( "cudaGetErrorString", [](const PaddleCudaError& error) -> std::string { return std::string(cudaGetErrorString(error.value)); }, "Get error string for cuda error"); cudart.def( "cudaGetErrorString", [](int error_code) -> std::string { return std::string( cudaGetErrorString(static_cast(error_code))); }, "Get error string for cuda error code"); cudart.def("cudaGetErrorString", cudaGetErrorString); cudart.def("cudaProfilerStart", #ifdef USE_ROCM []() -> PaddleCudaError { return PaddleCudaError(hipSuccess); } #else []() -> PaddleCudaError { py::gil_scoped_release no_gil; return PaddleCudaError(cudaProfilerStart()); } #endif ); cudart.def("cudaProfilerStop", #ifdef USE_ROCM []() -> PaddleCudaError { return PaddleCudaError(hipSuccess); } #else []() -> PaddleCudaError { py::gil_scoped_release no_gil; return PaddleCudaError(cudaProfilerStop()); } #endif ); cudart.def( "cudaHostRegister", [](uintptr_t ptr, size_t size, unsigned int flags) -> PaddleCudaError { py::gil_scoped_release no_gil; cudaError_t result = cudaHostRegister(reinterpret_cast(ptr), size, flags); return PaddleCudaError(result); }); cudart.def("cudaHostUnregister", [](uintptr_t ptr) -> PaddleCudaError { py::gil_scoped_release no_gil; cudaError_t result = cudaHostUnregister(reinterpret_cast(ptr)); return PaddleCudaError(result); }); cudart.def("cudaStreamCreate", [](uintptr_t ptr) -> PaddleCudaError { py::gil_scoped_release no_gil; cudaError_t result = cudaStreamCreate(reinterpret_cast(ptr)); return PaddleCudaError(result); }); cudart.def("cudaStreamDestroy", [](uintptr_t ptr) -> PaddleCudaError { py::gil_scoped_release no_gil; cudaError_t result = cudaStreamDestroy(reinterpret_cast(ptr)); return PaddleCudaError(result); }); #if !defined(USE_ROCM) && defined(CUDA_VERSION) && CUDA_VERSION < 12000 // cudaProfilerInitialize is no longer needed after CUDA 12: // https://forums.developer.nvidia.com/t/cudaprofilerinitialize-is-deprecated-alternative/200776/3 cudart.def( "cuda" "ProfilerInitialize", cudaProfilerInitialize, py::call_guard()); #endif cudart.def( "cuda" "MemGetInfo", [](int device) -> std::pair { const auto& place = GPUPlace(device); platform::CUDADeviceGuard cuda_guard(place); size_t device_free = 0; size_t device_total = 0; py::gil_scoped_release no_gil; cudaMemGetInfo(&device_free, &device_total); return {device_free, device_total}; }); } } // namespace pybind } // namespace paddle #endif // if defined(PADDLE_WITH_CUDA)