OpenCL设备唯一性

12
有没有一种方法可以让OpenCL给我一个所有可用OpenCL实现的唯一物理设备列表?我知道如何遍历平台/设备列表,但例如,在我的情况下,我有一个由英特尔提供的平台,它为我的CPU提供了高效的设备实现,并且APP平台为我的GPU提供了快速的实现,但是对于我的CPU而言则表现糟糕。
有没有办法确定这两个CPU设备实际上是同一台物理设备,以便我可以选择最有效的一个并与之合作,而不是同时使用它们并使它们在单个物理设备上争夺计算时间?
我已经查看了CL_DEVICE_VENDOR_ID和CL_DEVICE_NAME,但它们没有解决我的问题,CL_DEVICE_NAME将是相同型号的两个单独物理设备(双GPU)的相同,而CL_DEVICE_VENDOR_ID会根据平台为我的CPU提供不同的ID。
理想的解决方案是一些唯一的物理设备ID,但如果能手动更改OpenCL配置以自己重新安排设备的位置(如果可能的话),我会很高兴。

我不明白这个问题...所以你想在两个具有相同规格的CPU之间进行选择? - ardiyu07
我想使用所有可用的物理设备(针对易于并行化的问题)- 我只想在每个物理设备上使用单个逻辑设备,否则会出现争用。 - Thomas
5个回答

5
据我现在所了解的,目前没有可靠的解决方案。如果您所有的工作都是在单个进程中完成的,您可以使用clGetDeviceIDs返回的条目顺序或cl_device值本身(实际上它们是指针),但是如果您尝试在进程之间共享这些标识符,则情况会变得更糟。

请参见该人的博客文章,其中说:

问题在于,如果您有两个相同的GPU,则无法区分它们。如果调用clGetDeviceIDs,则返回它们的顺序实际上是未指定的,因此,如果第一个进程选择第一个设备,而第二个进程选择第二个设备,则它们都可能过度订阅同一GPU并使另一个GPU处于空闲状态。

然而,他指出nVidia和AMD提供了他们的自定义扩展,cl_amd_device_topologycl_nv_device_attribute_query。您可以检查您的设备是否支持这些扩展,并像原作者的代码一样使用它们:

// This cl_ext is provided as part of the AMD APP SDK
#include <CL/cl_ext.h>

cl_device_topology_amd topology;
status = clGetDeviceInfo (devices[i], CL_DEVICE_TOPOLOGY_AMD,
    sizeof(cl_device_topology_amd), &topology, NULL);

if(status != CL_SUCCESS) {
    // Handle error
}

if (topology.raw.type == CL_DEVICE_TOPOLOGY_TYPE_PCIE_AMD) {
    std::cout << "INFO: Topology: " << "PCI[ B#" << (int)topology.pcie.bus
        << ", D#" << (int)topology.pcie.device << ", F#"
        << (int)topology.pcie.function << " ]" << std::endl;
}

或者(由我编写的代码,改编自上面链接的帖子):
#define CL_DEVICE_PCI_BUS_ID_NV  0x4008
#define CL_DEVICE_PCI_SLOT_ID_NV 0x4009

cl_int bus_id;
cl_int slot_id;

status = clGetDeviceInfo (devices[i], CL_DEVICE_PCI_BUS_ID_NV,
    sizeof(cl_int), &bus_id, NULL);
if (status != CL_SUCCESS) {
    // Handle error.
}

status = clGetDeviceInfo (devices[i], CL_DEVICE_PCI_BUS_ID_NV,
    sizeof(cl_int), &slot_id, NULL);
if (status != CL_SUCCESS) {
    // Handle error.
}

std::cout << "Topology = [" << bus_id <<
                         ":"<< slot_id << "]" << std::endl;

它们返回的顺序实际上是不确定的,哇!这比我预期的还要糟糕。无论如何,我的问题并不是关于多个进程,而是关于不同平台在各自的平台上公开相同物理设备(例如,Intel SDK和AMD SDK都将相同的主CPU作为逻辑设备暴露出来),但这个拓扑扩展也解决了这个问题。谢谢回答! - Thomas
@Thomas:不客气!顺便说一下,clinfo程序应该显示nVidia和AMD设备的拓扑标识符。你绝对应该看一下他们是如何处理的,他们的代码似乎比我的好。 - firegurafiku

3
  • 如果您有属于同一平台的两个完全相同类型的设备,可以通过使用由clGetDeviceIDs返回的关联cl_device_ids来区分它们。

  • 如果您有可供两个不同平台使用的设备,则可以通过比较CL_DEVICE_NAME中的设备名称来消除第二个平台的条目。

  • 如果您想要找到设备的预期平台,请从clGetPlatformInfo()和clGetDeviceInfo中比较CL_PLATFORM_VENDOR和CL_DEVICE_VENDOR字符串中的字符串。

您可以将所有平台及其相关设备读入到单独的特定于平台的列表中,然后通过比较单独列表中的设备名称来消除重复项。这应该确保您不会在不同平台上获取相同的设备。

最后,您可以通过命令行参数或配置文件等方式,向您的应用程序提供参数,以将某种类型(CPU,GPU,加速器)的设备与特定平台相关联,如果存在设备类型的不同选择平台。希望这回答了您的问题。


0

对每张卡进行基准测试,例如gflops或每秒像素数。然后成对进行测试。如果任何一对的性能降至正常值的一半,或者它们的总和等于其中一个的最大值,则它们是同一物理设备。每个基准测试可以进行几毫秒,即使是40-GPU系统也只需要几秒钟就可以完成(对1600次所有配对进行暴力破解)。 (如果clGetDeviceIDs在某些时候失败)


0

综合以上答案,我的解决方案是:

long bus = 0; // leave it 0 for Intel
// update bus for NVIDIA/AMD ...
// ...
long uid = (bus << 5) | device_type;

变量bus根据NVIDIA / AMD设备特定信息查询进行计算,如firegurafiku所提到的,变量device_type是由clGetDeviceInfo(clDevice, CL_DEVICE_TYPE, sizeof(cl_device_type), &device_type, nullptr) API调用产生的结果,正如Steinin所建议的那样。

这种方法解决了在集成GPU的Intel CPU上具有相同唯一ID的问题。现在,两个设备都有唯一标识符,因为它们拥有不同的CL_DEVICE_TYPE

令人惊讶的是,在Oclgrind模拟设备上运行代码的情况下,Oclgrind simulator设备也得到了唯一标识符15,与我系统上的任何其他设备都不同。

唯一可能失败的情况是在单个主板上安装了几个相同型号的CPU时。


1
现在事情变得更加简化了,只要设备支持2个Khronos扩展,即可实现。第一个是cl_khr_device_uuid,它可以按照要求返回唯一UID。最后一个是cl_khr_pci_bus_info,它提供了一种平台无关的方法来提取PCI总线信息。搭载Intel HD Graphics的Intel Core i7 6700 HQ设备,最新驱动程序支持cl_khr_pci_bus_info。而搭载Nvidia GeForce 940MX的设备,最新驱动程序支持cl_khr_device_uuid - Mykyta Kozlov

0

无论如何,让我们假设您正在尝试获取所有设备的唯一ID,实际上您只需使用clGetDeviceIDs进行简单查询即可:

cl_int clGetDeviceIDs(cl_platform_id platform,
                      cl_device_type device_type,
                      cl_uint num_entries,
                      cl_device_id *devices,
                      cl_uint *num_devices)

然后您的设备列表将被插入到*devices数组中,然后您可以使用clGetDeviceInfo()来查找您想要使用的设备。


我想要使用它们所有,但我不希望任何物理设备被多个逻辑设备访问。 - Thomas
如果我没理解错的话,你想同时使用所有设备进行多线程处理?如果是这样,那么你可能需要查看CUDA的计算SDK和OpenCL的oclMultiThreads源代码,这里可以手动划分工作,然后异步地在可用的设备上运行它们。 - ardiyu07
不,我的问题比那更微妙。我知道我可以列出所有设备并对它们进行多线程处理。但问题在于单个物理设备(比如说,我的唯一CPU)会出现为两个逻辑设备(每个OpenCL平台一个)- 在这两个逻辑设备上进行多线程处理将导致唯一物理CPU的资源争用(对于GPU来说更是如此),因此我想检测这两个逻辑设备是否指向同一个物理设备,并且只使用其中一个。 - Thomas
啊,我终于明白你的问题了。我不知道你如何分区你的设备并让它们运行相同的OpenCL程序,但是还有一种方法可以使用OpenCL扩展将设备分成子设备,你可以在这里查看描述:http://www.khronos.org/registry/cl/extensions/ext/cl_ext_device_fission.txt。我认为它支持英特尔和AMD,但我不能保证它与你的环境兼容。 - ardiyu07

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接