Add uuid to XPU device properties (#161392)

# Motivation
Fix https://github.com/intel/torch-xpu-ops/issues/1955
Refer to https://github.com/intel/llvm/blob/sycl/sycl/doc/extensions/supported/sycl_ext_intel_device_info.md#device-uuid, `ext::intel::info::device::uuid` returns `std::array<unsigned char, 16>` as the UUID.
Pull Request resolved: https://github.com/pytorch/pytorch/pull/161392
Approved by: https://github.com/EikanWang, https://github.com/albanD
This commit is contained in:
Yu, Guangye
2025-09-01 14:58:29 +00:00
committed by PyTorch MergeBot
parent 8703debf66
commit f8746b878d
9 changed files with 73 additions and 46 deletions

View File

@ -115,19 +115,22 @@ namespace c10::xpu {
#define AT_FORALL_XPU_EXT_DEVICE_PROPERTIES(_) \ #define AT_FORALL_XPU_EXT_DEVICE_PROPERTIES(_) \
/* the number of EUs associated with the Intel GPU. */ \ /* the number of EUs associated with the Intel GPU. */ \
_(gpu_eu_count, 512) \ _(gpu_eu_count, gpu_eu_count, 512) \
\ \
/* the number of EUs in a subslice. */ \ /* the number of EUs in a subslice. */ \
_(gpu_eu_count_per_subslice, 8) \ _(gpu_eu_count_per_subslice, gpu_eu_count_per_subslice, 8) \
\ \
/* the simd width of EU of GPU. */ \ /* the simd width of EU of GPU. */ \
_(gpu_eu_simd_width, 8) \ _(gpu_eu_simd_width, gpu_eu_simd_width, 8) \
\ \
/* the number of hardware threads per EU of GPU. */ \ /* the number of hardware threads per EU of GPU. */ \
_(gpu_hw_threads_per_eu, 8) \ _(gpu_hw_threads_per_eu, gpu_hw_threads_per_eu, 8) \
\ \
/* the device identifier of the Intel GPU, also known as the product ID. */ \ /* the device identifier of the Intel GPU, also known as the product ID. */ \
_(device_id, 0) _(device_id, device_id, 0) \
\
/* the device descriptor for device Universal Unique ID, 16 bytes*/ \
_(uuid, device_info_uuid, (std::array<unsigned char, 16>{}))
#define AT_FORALL_XPU_DEVICE_ASPECT(_) \ #define AT_FORALL_XPU_DEVICE_ASPECT(_) \
/* sycl::half is supported on device. */ \ /* sycl::half is supported on device. */ \

View File

@ -157,8 +157,8 @@ void initDeviceProperties(DeviceProp* device_prop, DeviceIndex device) {
#define ASSIGN_DEVICE_PROP(property) \ #define ASSIGN_DEVICE_PROP(property) \
device_prop->property = raw_device.get_info<device::property>(); device_prop->property = raw_device.get_info<device::property>();
#define ASSIGN_EXT_DEVICE_PROP(property, default_value) \ #define ASSIGN_EXT_DEVICE_PROP(property, aspect_tag, default_value) \
device_prop->property = raw_device.has(sycl::aspect::ext_intel_##property) \ device_prop->property = raw_device.has(sycl::aspect::ext_intel_##aspect_tag) \
? raw_device.get_info<intel::info::device::property>() \ ? raw_device.get_info<intel::info::device::property>() \
: default_value; : default_value;

View File

@ -134,6 +134,10 @@ class TestXpu(TestCase):
device_properties.architecture, device_properties.architecture,
device_capability["architecture"], device_capability["architecture"],
) )
self.assertEqual(
len(str(device_properties.uuid)), 36
) # xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
self.assertEqual(len(device_properties.uuid.bytes), 16)
@unittest.skipIf(IS_WINDOWS, "not applicable to Windows (only fails with fork)") @unittest.skipIf(IS_WINDOWS, "not applicable to Windows (only fails with fork)")
def test_wrong_xpu_fork(self): def test_wrong_xpu_fork(self):

View File

@ -2389,6 +2389,7 @@ class _XpuDeviceProperties:
gpu_subslice_count: _int gpu_subslice_count: _int
architecture: _int architecture: _int
type: str type: str
uuid: Any
# Defined in torch/csrc/xpu/Stream.cpp # Defined in torch/csrc/xpu/Stream.cpp
class _XpuStreamBase(Stream): class _XpuStreamBase(Stream):

View File

@ -5,7 +5,6 @@
#include <c10/core/Device.h> #include <c10/core/Device.h>
#include <c10/core/TensorImpl.h> #include <c10/core/TensorImpl.h>
#include <c10/util/UniqueVoidPtr.h> #include <c10/util/UniqueVoidPtr.h>
#include <fmt/core.h>
#include <pybind11/pytypes.h> #include <pybind11/pytypes.h>
#include <torch/csrc/utils/python_arg_parser.h> #include <torch/csrc/utils/python_arg_parser.h>
#include <unordered_set> #include <unordered_set>
@ -1017,34 +1016,6 @@ PyObject* THCPModule_cudaGetSyncDebugMode(PyObject* self, PyObject* noargs) {
END_HANDLE_TH_ERRORS END_HANDLE_TH_ERRORS
} }
std::string uuid_to_string(const char* uuid_bytes) {
// UUIDs are a 128-bit label. CUDA and HIP store this as char[16].
// For string representation, the code here expands this to
// 8-4-4-4-12 hex format, so each byte becomes 2 hex characters.
return fmt::format(
"{:02x}{:02x}{:02x}{:02x}-"
"{:02x}{:02x}-"
"{:02x}{:02x}-"
"{:02x}{:02x}-"
"{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
(uint8_t)uuid_bytes[0],
(uint8_t)uuid_bytes[1],
(uint8_t)uuid_bytes[2],
(uint8_t)uuid_bytes[3],
(uint8_t)uuid_bytes[4],
(uint8_t)uuid_bytes[5],
(uint8_t)uuid_bytes[6],
(uint8_t)uuid_bytes[7],
(uint8_t)uuid_bytes[8],
(uint8_t)uuid_bytes[9],
(uint8_t)uuid_bytes[10],
(uint8_t)uuid_bytes[11],
(uint8_t)uuid_bytes[12],
(uint8_t)uuid_bytes[13],
(uint8_t)uuid_bytes[14],
(uint8_t)uuid_bytes[15]);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Cuda module initialization // Cuda module initialization
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -240,6 +240,34 @@ uint8_t storage_get(const at::Storage& self, ptrdiff_t idx) {
return self_t[idx].item<uint8_t>(); return self_t[idx].item<uint8_t>();
} }
std::string uuid_to_string(const char* uuid_bytes) {
// UUIDs are a 128-bit label. CUDA/HIP and XPU store this as char[16].
// For string representation, the code here expands this to
// 8-4-4-4-12 hex format, so each byte becomes 2 hex characters.
return fmt::format(
"{:02x}{:02x}{:02x}{:02x}-"
"{:02x}{:02x}-"
"{:02x}{:02x}-"
"{:02x}{:02x}-"
"{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
(uint8_t)uuid_bytes[0],
(uint8_t)uuid_bytes[1],
(uint8_t)uuid_bytes[2],
(uint8_t)uuid_bytes[3],
(uint8_t)uuid_bytes[4],
(uint8_t)uuid_bytes[5],
(uint8_t)uuid_bytes[6],
(uint8_t)uuid_bytes[7],
(uint8_t)uuid_bytes[8],
(uint8_t)uuid_bytes[9],
(uint8_t)uuid_bytes[10],
(uint8_t)uuid_bytes[11],
(uint8_t)uuid_bytes[12],
(uint8_t)uuid_bytes[13],
(uint8_t)uuid_bytes[14],
(uint8_t)uuid_bytes[15]);
}
template class THPPointer<THPStorage>; template class THPPointer<THPStorage>;
// NOLINTBEGIN(misc-use-internal-linkage) // NOLINTBEGIN(misc-use-internal-linkage)
namespace torch::gdb { namespace torch::gdb {

View File

@ -201,3 +201,5 @@ bool maybeThrowBackCompatKeepdimWarn(char* func);
void storage_fill(const at::Storage& self, uint8_t value); void storage_fill(const at::Storage& self, uint8_t value);
void storage_set(const at::Storage& self, ptrdiff_t idx, uint8_t value); void storage_set(const at::Storage& self, ptrdiff_t idx, uint8_t value);
uint8_t storage_get(const at::Storage& self, ptrdiff_t idx); uint8_t storage_get(const at::Storage& self, ptrdiff_t idx);
std::string uuid_to_string(const char* uuid_bytes);

View File

@ -295,8 +295,23 @@ static void registerXpuDeviceProperties(PyObject* module) {
return static_cast<int64_t>(prop.architecture); return static_cast<int64_t>(prop.architecture);
}; };
#endif #endif
// Wrapper class for XPU UUID
struct XPUuuid {
XPUuuid(const std::array<unsigned char, 16>& uuid) : bytes(uuid) {}
const std::array<unsigned char, 16>& bytes{};
};
auto m = py::handle(module).cast<py::module>(); auto m = py::handle(module).cast<py::module>();
py::class_<XPUuuid>(m, "_XPUuuid")
.def_property_readonly(
"bytes",
[](const XPUuuid& uuid) {
return std::vector<uint8_t>(uuid.bytes.begin(), uuid.bytes.end());
})
.def("__str__", [](const XPUuuid& uuid) {
return uuid_to_string(reinterpret_cast<const char*>(uuid.bytes.data()));
});
#define DEFINE_READONLY_MEMBER(member) \ #define DEFINE_READONLY_MEMBER(member) \
def_readonly(#member, &DeviceProp::member) def_readonly(#member, &DeviceProp::member)
@ -328,6 +343,9 @@ static void registerXpuDeviceProperties(PyObject* module) {
.def_property_readonly("architecture", get_device_architecture) .def_property_readonly("architecture", get_device_architecture)
#endif #endif
.def_property_readonly("type", get_device_type) .def_property_readonly("type", get_device_type)
.def_property_readonly(
"uuid",
[](const DeviceProp& prop) -> XPUuuid { return XPUuuid(prop.uuid); })
.def( .def(
"__repr__", "__repr__",
[&get_device_type, &gpu_subslice_count](const DeviceProp& prop) { [&get_device_type, &gpu_subslice_count](const DeviceProp& prop) {
@ -335,7 +353,9 @@ static void registerXpuDeviceProperties(PyObject* module) {
stream << "_XpuDeviceProperties(name='" << prop.name stream << "_XpuDeviceProperties(name='" << prop.name
<< "', platform_name='" << prop.platform_name << "', type='" << "', platform_name='" << prop.platform_name << "', type='"
<< get_device_type(prop) << "', device_id=0x" << std::hex << get_device_type(prop) << "', device_id=0x" << std::hex
<< std::uppercase << prop.device_id << std::dec << std::uppercase << prop.device_id << std::dec << ", uuid="
<< uuid_to_string(
reinterpret_cast<const char*>(prop.uuid.data()))
<< ", driver_version='" << prop.driver_version << ", driver_version='" << prop.driver_version
<< "', total_memory=" << "', total_memory="
<< prop.global_mem_size / (1024ull * 1024) << "MB" << prop.global_mem_size / (1024ull * 1024) << "MB"

View File

@ -236,15 +236,13 @@ def get_device_capability(device: Optional[_device_t] = None) -> dict[str, Any]:
Dict[str, Any]: the xpu capability dictionary of the device Dict[str, Any]: the xpu capability dictionary of the device
""" """
props = get_device_properties(device) props = get_device_properties(device)
# pybind service attributes are no longer needed and their presence breaks # Only keep attributes that are safe for dictionary serialization.
# the further logic related to the serialization of the created dictionary. serializable_types = (int, float, bool, str, type(None), list, tuple, dict)
# In particular it filters out `<bound method PyCapsule._pybind11_conduit_v1_ of _XpuDeviceProperties..>`
# to fix Triton tests.
# This field appears after updating pybind to 2.13.6.
return { return {
prop: getattr(props, prop) key: value
for prop in dir(props) for key in dir(props)
if not prop.startswith(("__", "_pybind11_")) if not key.startswith("__")
and isinstance((value := getattr(props, key)), serializable_types)
} }