Skip to content

Commit cea0bc2

Browse files
authored
Add paddle.device.cuda.get_device_properties (#35875)
* Initial Commit * fix py2 error * fix wrong words and doc * test=document_fix * fix _gpuDeviceProperties
1 parent 749bc24 commit cea0bc2

File tree

6 files changed

+211
-0
lines changed

6 files changed

+211
-0
lines changed

paddle/fluid/platform/gpu_info.cc

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ limitations under the License. */
1414

1515
#include "paddle/fluid/platform/gpu_info.h"
1616
#include <cstdlib>
17+
#include <mutex>
18+
#include <vector>
1719

1820
#include "gflags/gflags.h"
1921
#include "paddle/fluid/platform/cuda_device_guard.h"
@@ -39,6 +41,10 @@ DECLARE_uint64(gpu_memory_limit_mb);
3941

4042
constexpr static float fraction_reserve_gpu_memory = 0.05f;
4143

44+
static std::once_flag g_device_props_size_init_flag;
45+
static std::vector<std::unique_ptr<std::once_flag>> g_device_props_init_flags;
46+
static std::vector<paddle::gpuDeviceProp> g_device_props;
47+
4248
USE_GPU_MEM_STAT;
4349
namespace paddle {
4450
namespace platform {
@@ -297,6 +303,44 @@ std::vector<int> GetSelectedDevices() {
297303
return devices;
298304
}
299305

306+
const gpuDeviceProp &GetDeviceProperties(int id) {
307+
std::call_once(g_device_props_size_init_flag, [&] {
308+
int gpu_num = 0;
309+
gpu_num = platform::GetCUDADeviceCount();
310+
g_device_props_init_flags.resize(gpu_num);
311+
g_device_props.resize(gpu_num);
312+
for (int i = 0; i < gpu_num; ++i) {
313+
g_device_props_init_flags[i] = std::make_unique<std::once_flag>();
314+
}
315+
});
316+
317+
if (id == -1) {
318+
id = platform::GetCurrentDeviceId();
319+
}
320+
321+
if (id < 0 || id >= static_cast<int>(g_device_props.size())) {
322+
PADDLE_THROW(platform::errors::OutOfRange(
323+
"The device id %d is out of range [0, %d), where %d is the number of "
324+
"devices on this machine. Because the device id should be greater than "
325+
"or equal to zero and smaller than the number of gpus. Please input "
326+
"appropriate device again!",
327+
id, static_cast<int>(g_device_props.size()),
328+
static_cast<int>(g_device_props.size())));
329+
}
330+
331+
std::call_once(*(g_device_props_init_flags[id]), [&] {
332+
#ifdef PADDLE_WITH_CUDA
333+
PADDLE_ENFORCE_CUDA_SUCCESS(
334+
cudaGetDeviceProperties(&g_device_props[id], id));
335+
#else
336+
PADDLE_ENFORCE_CUDA_SUCCESS(
337+
hipGetDeviceProperties(&g_device_props[id], id));
338+
#endif
339+
});
340+
341+
return g_device_props[id];
342+
}
343+
300344
void SetDeviceId(int id) {
301345
// TODO(qijun): find a better way to cache the cuda device count
302346
PADDLE_ENFORCE_LT(id, GetCUDADeviceCount(),

paddle/fluid/platform/gpu_info.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ dim3 GetGpuMaxGridDimSize(int);
6767
//! Get a list of device ids from environment variable or use all.
6868
std::vector<int> GetSelectedDevices();
6969

70+
//! Get the properties of the ith GPU device.
71+
const gpuDeviceProp &GetDeviceProperties(int id);
72+
7073
//! Set the GPU device id for next execution.
7174
void SetDeviceId(int device_id);
7275

paddle/fluid/platform/type_defs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ namespace paddle {
2727
using gpuStream_t = hipStream_t;
2828
using gpuError_t = hipError_t;
2929
using gpuEvent_t = hipEvent_t;
30+
using gpuDeviceProp = hipDeviceProp_t;
3031
#else
3132
#define gpuSuccess cudaSuccess
3233
using gpuStream_t = cudaStream_t;
3334
using gpuError_t = cudaError_t;
3435
using gpuEvent_t = cudaEvent_t;
36+
using gpuDeviceProp = cudaDeviceProp;
3537
#endif
3638

3739
} // namespace paddle

paddle/fluid/pybind/pybind.cc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2286,6 +2286,31 @@ All parameter, weight, gradient are variables in Paddle.
22862286
#if defined(PADDLE_WITH_CUDA) || defined(PADDLE_WITH_HIP)
22872287
m.def("get_cuda_device_count", platform::GetCUDADeviceCount);
22882288
m.def("cuda_empty_cache", platform::EmptyCache);
2289+
m.def("get_device_properties",
2290+
[](int id) -> const gpuDeviceProp & {
2291+
return platform::GetDeviceProperties(id);
2292+
},
2293+
py::return_value_policy::copy);
2294+
2295+
py::class_<gpuDeviceProp>(m, "_gpuDeviceProperties")
2296+
.def_readonly("name", &gpuDeviceProp::name)
2297+
.def_readonly("major", &gpuDeviceProp::major)
2298+
.def_readonly("minor", &gpuDeviceProp::minor)
2299+
.def_readonly("is_multi_gpu_board", &gpuDeviceProp::isMultiGpuBoard)
2300+
.def_readonly("is_integrated", &gpuDeviceProp::integrated)
2301+
.def_readonly("multi_processor_count",
2302+
&gpuDeviceProp::multiProcessorCount)
2303+
.def_readonly("total_memory", &gpuDeviceProp::totalGlobalMem)
2304+
.def("__repr__", [](const gpuDeviceProp &gpu_device_prop) {
2305+
std::ostringstream stream;
2306+
stream << "_gpuDeviceProperties(name='" << gpu_device_prop.name
2307+
<< "', major=" << gpu_device_prop.major
2308+
<< ", minor=" << gpu_device_prop.minor << ", total_memory="
2309+
<< gpu_device_prop.totalGlobalMem / (1024 * 1024)
2310+
<< "MB, multi_processor_count="
2311+
<< gpu_device_prop.multiProcessorCount << ")";
2312+
return stream.str();
2313+
});
22892314

22902315
#if !defined(PADDLE_WITH_HIP) && !defined(_WIN32)
22912316
m.def("nvprof_init", platform::CudaProfilerInit);

python/paddle/device/cuda/__init__.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
'device_count',
2828
'empty_cache',
2929
'stream_guard',
30+
'get_device_properties',
3031
]
3132

3233

@@ -204,3 +205,69 @@ def stream_guard(stream):
204205
yield
205206
finally:
206207
stream = _set_current_stream(pre_stream)
208+
209+
210+
def get_device_properties(device=None):
211+
'''
212+
Return the properties of given device.
213+
214+
Args:
215+
device(paddle.CUDAPlace or int or str): The device, the id of the device
216+
or the string name of device like 'gpu:x' which to get the properties of
217+
the device from. If device is None, the device is the current device.
218+
Default: None.
219+
220+
Returns:
221+
_gpuDeviceProperties: the properties of the device which include ASCII string
222+
identifying device, major compute capability, minor compute capability, global
223+
memory available on device and the number of multiprocessors on the device.
224+
225+
Examples:
226+
227+
.. code-block:: python
228+
229+
# required: gpu
230+
231+
import paddle
232+
paddle.device.cuda.get_device_properties()
233+
# _gpuDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108)
234+
235+
paddle.device.cuda.get_device_properties(0)
236+
# _gpuDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108)
237+
238+
paddle.device.cuda.get_device_properties('gpu:0')
239+
# _gpuDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108)
240+
241+
paddle.device.cuda.get_device_properties(paddle.CUDAPlace(0))
242+
# _gpuDeviceProperties(name='A100-SXM4-40GB', major=8, minor=0, total_memory=40536MB, multi_processor_count=108)
243+
244+
'''
245+
246+
if not core.is_compiled_with_cuda():
247+
raise ValueError(
248+
"The API paddle.device.cuda.get_device_properties is not supported in "
249+
"CPU-only PaddlePaddle. Please reinstall PaddlePaddle with GPU support "
250+
"to call this API.")
251+
252+
if device is not None:
253+
if isinstance(device, int):
254+
device_id = device
255+
elif isinstance(device, core.CUDAPlace):
256+
device_id = device.get_device_id()
257+
elif isinstance(device, str):
258+
if device.startswith('gpu:'):
259+
device_id = int(device[4:])
260+
else:
261+
raise ValueError(
262+
"The current string {} is not expected. Because paddle.device."
263+
"cuda.get_device_properties only support string which is like 'gpu:x'. "
264+
"Please input appropriate string again!".format(device))
265+
else:
266+
raise ValueError(
267+
"The device type {} is not expected. Because paddle.device.cuda."
268+
"get_device_properties only support int, str or paddle.CUDAPlace. "
269+
"Please input appropriate device again!".format(device))
270+
else:
271+
device_id = -1
272+
273+
return core.get_device_properties(device_id)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import paddle
16+
import unittest
17+
from paddle.fluid import core
18+
from paddle.device.cuda import device_count, get_device_properties
19+
20+
21+
class TestGetDeviceProperties(unittest.TestCase):
22+
def test_get_device_properties_default(self):
23+
if core.is_compiled_with_cuda():
24+
props = get_device_properties()
25+
self.assertIsNotNone(props)
26+
27+
def test_get_device_properties_str(self):
28+
if core.is_compiled_with_cuda():
29+
props = get_device_properties('gpu:0')
30+
self.assertIsNotNone(props)
31+
32+
def test_get_device_properties_int(self):
33+
if core.is_compiled_with_cuda():
34+
gpu_num = device_count()
35+
for i in range(gpu_num):
36+
props = get_device_properties(i)
37+
self.assertIsNotNone(props)
38+
39+
def test_get_device_properties_CUDAPlace(self):
40+
if core.is_compiled_with_cuda():
41+
device = core.CUDAPlace(0)
42+
props = get_device_properties(device)
43+
self.assertIsNotNone(props)
44+
45+
46+
class TestGetDevicePropertiesError(unittest.TestCase):
47+
def test_error_api(self):
48+
if core.is_compiled_with_cuda():
49+
50+
def test_device_indexError_error():
51+
device_error = device_count() + 1
52+
props = get_device_properties(device_error)
53+
54+
self.assertRaises(IndexError, test_device_indexError_error)
55+
56+
def test_device_value_error1():
57+
device_error = 'gpu1'
58+
props = get_device_properties(device_error)
59+
60+
self.assertRaises(ValueError, test_device_value_error1)
61+
62+
def test_device_value_error2():
63+
device_error = float(device_count())
64+
props = get_device_properties(device_error)
65+
66+
self.assertRaises(ValueError, test_device_value_error2)
67+
68+
69+
if __name__ == "__main__":
70+
unittest.main()

0 commit comments

Comments
 (0)