From c5aaec48ac9d9617806f89ebc677e51ac4834b11 Mon Sep 17 00:00:00 2001 From: Yanxing-Shi Date: Sat, 18 Sep 2021 11:35:57 +0000 Subject: [PATCH 1/5] Initial Commit --- paddle/fluid/platform/gpu_info.cc | 44 ++++++++++++ paddle/fluid/platform/gpu_info.h | 3 + paddle/fluid/platform/type_defs.h | 2 + paddle/fluid/pybind/pybind.cc | 25 +++++++ python/paddle/__init__.py | 1 + python/paddle/device/cuda/__init__.py | 58 +++++++++++++++ .../unittests/test_get_device_properties.py | 70 +++++++++++++++++++ 7 files changed, 203 insertions(+) create mode 100644 python/paddle/fluid/tests/unittests/test_get_device_properties.py diff --git a/paddle/fluid/platform/gpu_info.cc b/paddle/fluid/platform/gpu_info.cc index 76edb3910ccced..c4ac5aa3046a9c 100644 --- a/paddle/fluid/platform/gpu_info.cc +++ b/paddle/fluid/platform/gpu_info.cc @@ -14,6 +14,8 @@ limitations under the License. */ #include "paddle/fluid/platform/gpu_info.h" #include +#include +#include #include "gflags/gflags.h" #include "paddle/fluid/platform/cuda_device_guard.h" @@ -39,6 +41,10 @@ DECLARE_uint64(gpu_memory_limit_mb); constexpr static float fraction_reserve_gpu_memory = 0.05f; +static std::once_flag g_device_props_size_init_flag; +static std::vector> g_device_props_init_flags; +static std::vector g_device_props; + USE_GPU_MEM_STAT; namespace paddle { namespace platform { @@ -297,6 +303,44 @@ std::vector GetSelectedDevices() { return devices; } +const gpuDeviceProp &GetDeviceProperties(int id) { + std::call_once(g_device_props_size_init_flag, [&] { + int gpu_num = 0; + gpu_num = platform::GetCUDADeviceCount(); + g_device_props_init_flags.resize(gpu_num); + g_device_props.resize(gpu_num); + for (int i = 0; i < gpu_num; ++i) { + g_device_props_init_flags[i] = std::make_unique(); + } + }); + + if (id == -1) { + id = platform::GetCurrentDeviceId(); + } + + if (id < 0 || id >= static_cast(g_device_props.size())) { + PADDLE_THROW(platform::errors::OutOfRange( + "The device id %d is out of range [0, %d), where %d is the number of " + "devices on this machine. Because the device id should be greater than " + "or equal to zero and smaller than the number of gpus. Please input " + "appropriate device again!", + id, static_cast(g_device_props.size()), + static_cast(g_device_props.size()))); + } + + std::call_once(*(g_device_props_init_flags[id]), [&] { +#ifdef PADDLE_WITH_CUDA + PADDLE_ENFORCE_CUDA_SUCCESS( + cudaGetDeviceProperties(&g_device_props[id], id)); +#else + PADDLE_ENFORCE_CUDA_SUCCESS( + hipGetDeviceProperties(&g_device_props[id], id)); +#endif + }); + + return g_device_props[id]; +} + void SetDeviceId(int id) { // TODO(qijun): find a better way to cache the cuda device count PADDLE_ENFORCE_LT(id, GetCUDADeviceCount(), diff --git a/paddle/fluid/platform/gpu_info.h b/paddle/fluid/platform/gpu_info.h index ef7f93a61dbfb3..401873dcd77da2 100644 --- a/paddle/fluid/platform/gpu_info.h +++ b/paddle/fluid/platform/gpu_info.h @@ -67,6 +67,9 @@ dim3 GetGpuMaxGridDimSize(int); //! Get a list of device ids from environment variable or use all. std::vector GetSelectedDevices(); +//! Get the properties of the ith GPU device. +const gpuDeviceProp &GetDeviceProperties(int id); + //! Set the GPU device id for next execution. void SetDeviceId(int device_id); diff --git a/paddle/fluid/platform/type_defs.h b/paddle/fluid/platform/type_defs.h index 31784a04265803..f46bd1a0bdfa4a 100644 --- a/paddle/fluid/platform/type_defs.h +++ b/paddle/fluid/platform/type_defs.h @@ -27,11 +27,13 @@ namespace paddle { using gpuStream_t = hipStream_t; using gpuError_t = hipError_t; using gpuEvent_t = hipEvent_t; +using gpuDeviceProp = hipDeviceProp_t; #else #define gpuSuccess cudaSuccess using gpuStream_t = cudaStream_t; using gpuError_t = cudaError_t; using gpuEvent_t = cudaEvent_t; +using gpuDeviceProp = cudaDeviceProp; #endif } // namespace paddle diff --git a/paddle/fluid/pybind/pybind.cc b/paddle/fluid/pybind/pybind.cc index e404f27a10d0b1..feb6fd3cfec3fb 100644 --- a/paddle/fluid/pybind/pybind.cc +++ b/paddle/fluid/pybind/pybind.cc @@ -2283,6 +2283,31 @@ All parameter, weight, gradient are variables in Paddle. #if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP) m.def("get_cuda_device_count", platform::GetCUDADeviceCount); m.def("cuda_empty_cache", platform::EmptyCache); + m.def("get_device_properties", + [](int id) -> const gpuDeviceProp & { + return platform::GetDeviceProperties(id); + }, + py::return_value_policy::copy); + + py::class_(m, "_gpuDeviceProperties") + .def_readonly("name", &gpuDeviceProp::name) + .def_readonly("major", &gpuDeviceProp::major) + .def_readonly("minor", &gpuDeviceProp::minor) + .def_readonly("is_multi_gpu_board", &gpuDeviceProp::isMultiGpuBoard) + .def_readonly("is_integrated", &gpuDeviceProp::integrated) + .def_readonly("multi_processor_count", + &gpuDeviceProp::multiProcessorCount) + .def_readonly("total_memory", &gpuDeviceProp::totalGlobalMem) + .def("__repr__", [](const gpuDeviceProp &gpu_device_prop) { + std::ostringstream stream; + stream << "_CudaDeviceProperties(name='" << gpu_device_prop.name + << "', major=" << gpu_device_prop.major + << ", minor=" << gpu_device_prop.minor << ", total_memory=" + << gpu_device_prop.totalGlobalMem / (1024 * 1024) + << "MB, multi_processor_count=" + << gpu_device_prop.multiProcessorCount << ")"; + return stream.str(); + }); #if !defined(PADDLE_WITH_HIP) && !defined(_WIN32) m.def("nvprof_init", platform::CudaProfilerInit); diff --git a/python/paddle/__init__.py b/python/paddle/__init__.py index 6bd58ee558f0be..d9efc7da103516 100755 --- a/python/paddle/__init__.py +++ b/python/paddle/__init__.py @@ -335,6 +335,7 @@ 'save', 'multinomial', 'get_cuda_rng_state', + 'get_device_properties', 'rank', 'empty_like', 'eye', diff --git a/python/paddle/device/cuda/__init__.py b/python/paddle/device/cuda/__init__.py index 4d1934aeed9fb5..c209c9ce49a5ce 100644 --- a/python/paddle/device/cuda/__init__.py +++ b/python/paddle/device/cuda/__init__.py @@ -27,6 +27,7 @@ 'device_count', 'empty_cache', 'stream_guard', + 'get_device_properties', ] @@ -204,3 +205,60 @@ def stream_guard(stream): yield finally: stream = _set_current_stream(pre_stream) + + +def get_device_properties(device=None): + ''' + Return the properties of given CUDA device. + Args: + device(paddle.CUDAPlace() or int or str): The device, the ID of the device + or the string name of device like 'gpu:x' which to get the properties of + the device from. If device is None, the device is the current device. + Default: None. + Returns: + _CudaDeviceProperties: the properties of the device which include ASCII string + identifying device, major compute capability, minor compute capability, global + memory available on device in bytes and the number of multiprocessors on the device. + Examples: + + .. code-block:: python + # required: gpu + import paddle + paddle.device.cuda.get_device_properties() + # _CudaDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108) + paddle.device.cuda.get_device_properties(0) + # _CudaDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108) + paddle.device.cuda.get_device_properties('gpu:0') + # _CudaDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108) + paddle.device.cuda.get_device_properties(paddle.CUDAPlace(0)) + # _CudaDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108) + ''' + + if not core.is_compiled_with_cuda(): + raise ValueError( + "The current device {} is not expected. Because paddle.device.cuda." + "get_device_properties only support cuda device. Please change device" + "and input device again!".format(device)) + + device_id = -1 + + if device is not None: + if isinstance(device, int): + device_id = device + elif isinstance(device, core.CUDAPlace): + device_id = device.get_device_id() + elif isinstance(device, str): + if device.startswith('gpu:'): + device_id = int(device[4:]) + else: + raise ValueError( + "The current string {} is not expected. Because paddle.device." + "cuda.get_device_properties only support string which is like 'gpu:x'. " + "Please input appropriat string again!".format(device)) + else: + raise ValueError( + "The device type {} is not expected. Because paddle.device.cuda." + "get_device_properties only support int, str or paddle.CUDAPlace. " + "Please input appropriate device again!".format(device)) + + return core.get_device_properties(device_id) diff --git a/python/paddle/fluid/tests/unittests/test_get_device_properties.py b/python/paddle/fluid/tests/unittests/test_get_device_properties.py new file mode 100644 index 00000000000000..4cfb91bfae93e7 --- /dev/null +++ b/python/paddle/fluid/tests/unittests/test_get_device_properties.py @@ -0,0 +1,70 @@ +# Copyright (c) 2021 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. + +import paddle +import unittest +from paddle.fluid import core +from paddle.device.cuda import device_count, get_device_properties + + +class TestGetDeviceProperties(unittest.TestCase): + def test_get_device_properties_default(self): + if core.is_compiled_with_cuda(): + props = get_device_properties() + self.assertIsNotNone(props) + + def test_get_device_properties_str(self): + if core.is_compiled_with_cuda(): + props = get_device_properties('gpu:0') + self.assertIsNotNone(props) + + def test_get_device_properties_int(self): + if core.is_compiled_with_cuda(): + gpu_num = device_count() + for i in range(gpu_num): + props = get_device_properties(i) + self.assertIsNotNone(props) + + def test_get_device_properties_CUDAPlace(self): + if core.is_compiled_with_cuda(): + device = core.CUDAPlace(0) + props = get_device_properties(device) + self.assertIsNotNone(props) + + +class TestGetDevicePropertiesError(unittest.TestCase): + def test_error_api(self): + if core.is_compiled_with_cuda(): + + def test_device_indexError_error(): + device_error = device_count() + 1 + props = get_device_properties(device_error) + + self.assertRaises(IndexError, test_device_indexError_error) + + def test_device_value_error1(): + device_error = 'gpu1' + props = get_device_properties(device_error) + + self.assertRaises(ValueError, test_device_value_error1) + + def test_device_value_error2(): + device_error = float(device_count()) + props = get_device_properties(device_error) + + self.assertRaises(ValueError, test_device_value_error2) + + +if __name__ == "__main__": + unittest.main() From 7a402c6362cc0c1189bf34aac0a2b64f518dac3a Mon Sep 17 00:00:00 2001 From: Yanxing-Shi Date: Wed, 22 Sep 2021 05:04:15 +0000 Subject: [PATCH 2/5] fix py2 error --- python/paddle/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/paddle/__init__.py b/python/paddle/__init__.py index d9efc7da103516..6bd58ee558f0be 100755 --- a/python/paddle/__init__.py +++ b/python/paddle/__init__.py @@ -335,7 +335,6 @@ 'save', 'multinomial', 'get_cuda_rng_state', - 'get_device_properties', 'rank', 'empty_like', 'eye', From 5b7d3a27ab980b189add3ab44dde39787cc09659 Mon Sep 17 00:00:00 2001 From: Yanxing-Shi Date: Sun, 26 Sep 2021 08:06:03 +0000 Subject: [PATCH 3/5] fix wrong words and doc --- python/paddle/device/cuda/__init__.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/python/paddle/device/cuda/__init__.py b/python/paddle/device/cuda/__init__.py index c209c9ce49a5ce..ee563af2eea16e 100644 --- a/python/paddle/device/cuda/__init__.py +++ b/python/paddle/device/cuda/__init__.py @@ -211,7 +211,7 @@ def get_device_properties(device=None): ''' Return the properties of given CUDA device. Args: - device(paddle.CUDAPlace() or int or str): The device, the ID of the device + device(paddle.CUDAPlace or int or str): The device, the ID of the device or the string name of device like 'gpu:x' which to get the properties of the device from. If device is None, the device is the current device. Default: None. @@ -236,11 +236,9 @@ def get_device_properties(device=None): if not core.is_compiled_with_cuda(): raise ValueError( - "The current device {} is not expected. Because paddle.device.cuda." - "get_device_properties only support cuda device. Please change device" - "and input device again!".format(device)) - - device_id = -1 + "The API paddle.device.cuda.get_device_properties is not supported in " + "CPU-only PaddlePaddle. Please reinstall PaddlePaddle with GPU support " + "to call this API.") if device is not None: if isinstance(device, int): @@ -254,11 +252,13 @@ def get_device_properties(device=None): raise ValueError( "The current string {} is not expected. Because paddle.device." "cuda.get_device_properties only support string which is like 'gpu:x'. " - "Please input appropriat string again!".format(device)) + "Please input appropriate string again!".format(device)) else: raise ValueError( "The device type {} is not expected. Because paddle.device.cuda." "get_device_properties only support int, str or paddle.CUDAPlace. " "Please input appropriate device again!".format(device)) + else: + device_id = -1 return core.get_device_properties(device_id) From 92f6a0a1158e1f60667271a7ab4938f3dd9cdb5a Mon Sep 17 00:00:00 2001 From: Yanxing-Shi Date: Mon, 27 Sep 2021 02:56:45 +0000 Subject: [PATCH 4/5] test=document_fix --- python/paddle/device/cuda/__init__.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/python/paddle/device/cuda/__init__.py b/python/paddle/device/cuda/__init__.py index ee563af2eea16e..bc22d65bccd7ee 100644 --- a/python/paddle/device/cuda/__init__.py +++ b/python/paddle/device/cuda/__init__.py @@ -210,28 +210,37 @@ def stream_guard(stream): def get_device_properties(device=None): ''' Return the properties of given CUDA device. + Args: device(paddle.CUDAPlace or int or str): The device, the ID of the device or the string name of device like 'gpu:x' which to get the properties of the device from. If device is None, the device is the current device. Default: None. + Returns: - _CudaDeviceProperties: the properties of the device which include ASCII string + _gpuDeviceProperties: the properties of the device which include ASCII string identifying device, major compute capability, minor compute capability, global memory available on device in bytes and the number of multiprocessors on the device. + Examples: .. code-block:: python + # required: gpu + import paddle paddle.device.cuda.get_device_properties() # _CudaDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108) + paddle.device.cuda.get_device_properties(0) # _CudaDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108) + paddle.device.cuda.get_device_properties('gpu:0') # _CudaDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108) + paddle.device.cuda.get_device_properties(paddle.CUDAPlace(0)) # _CudaDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108) + ''' if not core.is_compiled_with_cuda(): From 262a3c635ea7dac64bd79ccb96138e3440222619 Mon Sep 17 00:00:00 2001 From: Yanxing-Shi Date: Mon, 27 Sep 2021 05:54:16 +0000 Subject: [PATCH 5/5] fix _gpuDeviceProperties --- paddle/fluid/pybind/pybind.cc | 2 +- python/paddle/device/cuda/__init__.py | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/paddle/fluid/pybind/pybind.cc b/paddle/fluid/pybind/pybind.cc index feb6fd3cfec3fb..8b9c70b1f440ec 100644 --- a/paddle/fluid/pybind/pybind.cc +++ b/paddle/fluid/pybind/pybind.cc @@ -2300,7 +2300,7 @@ All parameter, weight, gradient are variables in Paddle. .def_readonly("total_memory", &gpuDeviceProp::totalGlobalMem) .def("__repr__", [](const gpuDeviceProp &gpu_device_prop) { std::ostringstream stream; - stream << "_CudaDeviceProperties(name='" << gpu_device_prop.name + stream << "_gpuDeviceProperties(name='" << gpu_device_prop.name << "', major=" << gpu_device_prop.major << ", minor=" << gpu_device_prop.minor << ", total_memory=" << gpu_device_prop.totalGlobalMem / (1024 * 1024) diff --git a/python/paddle/device/cuda/__init__.py b/python/paddle/device/cuda/__init__.py index bc22d65bccd7ee..a559df21ad2413 100644 --- a/python/paddle/device/cuda/__init__.py +++ b/python/paddle/device/cuda/__init__.py @@ -209,10 +209,10 @@ def stream_guard(stream): def get_device_properties(device=None): ''' - Return the properties of given CUDA device. + Return the properties of given device. Args: - device(paddle.CUDAPlace or int or str): The device, the ID of the device + device(paddle.CUDAPlace or int or str): The device, the id of the device or the string name of device like 'gpu:x' which to get the properties of the device from. If device is None, the device is the current device. Default: None. @@ -220,7 +220,7 @@ def get_device_properties(device=None): Returns: _gpuDeviceProperties: the properties of the device which include ASCII string identifying device, major compute capability, minor compute capability, global - memory available on device in bytes and the number of multiprocessors on the device. + memory available on device and the number of multiprocessors on the device. Examples: @@ -230,16 +230,16 @@ def get_device_properties(device=None): import paddle paddle.device.cuda.get_device_properties() - # _CudaDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108) + # _gpuDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108) paddle.device.cuda.get_device_properties(0) - # _CudaDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108) + # _gpuDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108) paddle.device.cuda.get_device_properties('gpu:0') - # _CudaDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108) + # _gpuDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108) paddle.device.cuda.get_device_properties(paddle.CUDAPlace(0)) - # _CudaDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108) + # _gpuDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108) '''