【问题标题】:detecting NVIDIA GPUs without CUDA检测没有 CUDA 的 NVIDIA GPU
【发布时间】:2012-10-10 21:14:33
【问题描述】:

我想在不链接到 CUDA 库的情况下提取一组相当有限的关于 NVIDIA GPU 的信息。 需要的唯一信息是 GPU 的计算能力和名称,除此之外可能有用但不是必需的。代码应该用 C(或 C++)编写。该信息将在配置时(当 CUDA 工具包不可用时)和运行时(当执行的二进制文件未使用 CUDA 支持编译时)用于向用户建议系统中存在受支持的 GPU。

据我了解,这可以通过驱动程序 API 实现,但我不太熟悉这需要什么技术细节。所以我的问题是:

  • 至少满足最低要求的具体步骤是什么(见上文);

  • 有这样的开源代码吗?

请注意,我的第一步是为 Linux 编写一些代码,但最终我需要独立于平台的代码。考虑到 CUDA 的平台可用性,对于一个完整的解决方案,这将涉及适用于 Linux、Mac OS 和 Windows 的 x86/AMD64 上的代码(至少目前,该列表很快就会随着 ARM 得到扩展)。

编辑

我所说的“可以通过驱动程序 API”的意思是应该能够动态加载 libcuda.so 并通过驱动程序 API 查询设备属性。不过,我不确定细节。

【问题讨论】:

  • 您是否考虑过Tesla Deployment kit(包括您提到的查询的源代码示例,但有一些限制)?
  • @RobertCrovella 我有,但我不太喜欢分发二进制文件的想法。其他缺点是什么?
  • 您指的是哪些二进制文件需要分发? libnvidia-ml.so 由 nvidia 驱动程序安装(如果它是“最近的”驱动程序)。因此,在不试图在评论中涵盖所有内容的情况下,限制是:1. 需要安装适当的“最新”nvidia 驱动程序,2. 提供的 GeForce 产品信息可能不如 Quadro 和 Tesla GPU(尽管这部分是 GeForce 限制,例如不支持 ECC)。
  • 出于好奇,为什么不使用 Cuda 工具包进行构建是禁忌。因为这实际上是有史以来最简单的事情。
  • CUDA SDK 中包含的 CUDA 示例中有一个 deviceQueryDrvAPI 示例(从 CUDA 5.0 开始,包含在 CUDA 工具包分发中)。这向您展示了如何使用驱动程序 API 查询您需要的内容。但是所有建议的解决方案都需要“链接到 CUDA 库”。我怀疑你只是说你不想链接到 CUDA 运行时 API 库 (cudart),因为这需要你分发 cudart 共享库。

标签: c cuda


【解决方案1】:

很遗憾,NVML 不提供有关设备计算能力的信息。

你需要做的是:

  1. 手动加载 CUDA 库(应用程序未链接到 libcuda)
    • 如果库不存在,则未安装 CUDA 驱动程序
  2. 在库中找到指向必要函数的指针
  3. 使用驱动程序 API 查询有关可用 GPU 的信息

我希望这段代码会有所帮助。我已经在 Linux 下对其进行了测试,但稍作修改后,它也应该可以在 Windows 下编译。

#include <cuda.h>
#include <stdio.h>

#ifdef WINDOWS
#include <Windows.h>
#else
#include <dlfcn.h>
#endif


void * loadCudaLibrary() {
#ifdef WINDOWS
    return LoadLibraryA("nvcuda.dll");
#else
    return dlopen ("libcuda.so", RTLD_NOW);
#endif
}

void (*getProcAddress(void * lib, const char *name))(void){
#ifdef WINDOWS
    return (void (*)(void)) GetProcAddress(lib, name);
#else
    return (void (*)(void)) dlsym(lib,(const char *)name);
#endif
}

int freeLibrary(void *lib)
{
#ifdef WINDOWS
    return FreeLibrary(lib);
#else
    return dlclose(lib);
#endif
}

typedef CUresult CUDAAPI (*cuInit_pt)(unsigned int Flags);
typedef CUresult CUDAAPI (*cuDeviceGetCount_pt)(int *count);
typedef CUresult CUDAAPI (*cuDeviceComputeCapability_pt)(int *major, int *minor, CUdevice dev);

int main() {
    void * cuLib;
    cuInit_pt my_cuInit = NULL;
    cuDeviceGetCount_pt my_cuDeviceGetCount = NULL;
    cuDeviceComputeCapability_pt my_cuDeviceComputeCapability = NULL;

    if ((cuLib = loadCudaLibrary()) == NULL)
        return 1; // cuda library is not present in the system

    if ((my_cuInit = (cuInit_pt) getProcAddress(cuLib, "cuInit")) == NULL)
        return 1; // sth is wrong with the library
    if ((my_cuDeviceGetCount = (cuDeviceGetCount_pt) getProcAddress(cuLib, "cuDeviceGetCount")) == NULL)
        return 1; // sth is wrong with the library
    if ((my_cuDeviceComputeCapability = (cuDeviceComputeCapability_pt) getProcAddress(cuLib, "cuDeviceComputeCapability")) == NULL)
        return 1; // sth is wrong with the library

    {
        int count, i;
        if (CUDA_SUCCESS != my_cuInit(0))
            return 1; // failed to initialize
        if (CUDA_SUCCESS != my_cuDeviceGetCount(&count))
            return 1; // failed

        for (i = 0; i < count; i++)
        {
            int major, minor;
            if (CUDA_SUCCESS != my_cuDeviceComputeCapability(&major, &minor, i))
                return 1; // failed

            printf("dev %d CUDA compute capability major %d minor %d\n", i, major, minor);
        }
    }
    freeLibrary(cuLib);
    return 0; 
}

在 Linux 上测试:

$ gcc -ldl main.c
$ ./a.out
dev 0 CUDA compute capability major 2 minor 0
dev 1 CUDA compute capability major 2 minor 0

在没有CUDA驱动的linux上测试

$ ./a.out
$ echo $?
1

干杯

【讨论】:

  • 谢谢,这看起来很有希望!现在唯一的问题是这段代码包含 cuda.h,而我的目标是在配置时编译这么一小段代码,检查兼容的硬件,如果发现任何有用的东西就警告用户。所以假设主机上没有安装CUDA工具包。我在 SDK 中找到了一个 cuda_drvapi_dynlink_cuda.h。我想,从中提取所需的代码可以完成这项工作,我唯一需要弄清楚的是许可(必须与 LGPL 兼容)。
  • 你不能把这个应用程序作为二进制文件发布吗?它可以仅使用 C 编译器进行编译,因此您不会遇到任何 CLI 不兼容问题,例如使用 C++(CUDA 运行时 API 需要 C++ 编译器,但不需要 CUDA 驱动程序 API)。我不确定在配置时编译此应用程序的要求来自哪里。
  • 不,我不能。这是一个科学的 HPC 代码,几乎可以在从上网本到最大的超级计算机的所有硬件上编译。一个重要方面是避免用户必须解决的十几个错误消息(包括缺少 CUDA 工具包),并且仅在配置或运行时检测到支持的设备时警告用户。
  • 我的意思是你不能发布一个以二进制形式搜索 CUDA 设备的应用程序。它可以用 gcc 编译,并且可以在任何机器上工作,无需重新编译。只需在配置步骤中运行此发现应用程序。
  • 在 Windows 10 上,我不需要包含 cuda.h 并且上面的示例有效。
【解决方案2】:

这些人肯定知道答案:

http://www.ozone3d.net/gpu_caps_viewer

但我只能知道我可以通过安装 CUDA 或 OpenCL 来完成。

我认为一种方法可以直接使用 OpenGL,也许这就是你所说的驱动 API,但我只能给你这些例子(需要 CUDA):

http://www.naic.edu/~phil/hardware/nvidia/doc/src/deviceQuery/deviceQuery.cpp

【讨论】:

    【解决方案3】:

    首先,我认为NVIDIA NVML 是您正在寻找的API。其次,有一个基于 NVML 的开源项目,名为PAPI NVML

    【讨论】:

    • 谢谢。正如我在上面的评论中提到的,NVIDIA NVML 不是一个好的候选者,因为 AFAIK 它不提供有关计算能力的信息。不过,我会看看 PAPI NVML,但我怀疑它也不会提供此信息。
    【解决方案4】:

    我通过使用和静态链接 CUDA 6.0 SDK 解决了这个问题。它生成的应用程序在没有 NVIDIA 卡的机器或未安装 SDK 的机器上也能正常工作。在这种情况下,它将表明支持 CUDA 的设备为零。

    在 CUDA SDK 附带的示例中有一个名为 deviceQuery 的示例 - 我使用其中的 sn-ps 来编写以下代码。我决定是否存在支持 CUDA 的设备,如果存在,哪些设备具有最高的计算能力:

    #include <cuda_runtime.h>
    
    struct GpuCap
    {
        bool QueryFailed;           // True on error
        int  DeviceCount;           // Number of CUDA devices found 
        int  StrongestDeviceId;     // ID of best CUDA device
        int  ComputeCapabilityMajor; // Major compute capability (of best device)
        int  ComputeCapabilityMinor; // Minor compute capability
    };
    
    GpuCap GetCapabilities()
    {
        GpuCap gpu;
        gpu.QueryFailed = false;
        gpu.StrongestDeviceId = -1;
        gpu.ComputeCapabilityMajor = -1;
        gpu.ComputeCapabilityMinor = -1;
    
        cudaError_t error_id = cudaGetDeviceCount(&gpu.DeviceCount);
        if (error_id != cudaSuccess)
        {
            gpu.QueryFailed = true;
            gpu.DeviceCount = 0;    
            return gpu;
        }
    
        if (gpu.DeviceCount == 0)
            return gpu; // "There are no available device(s) that support CUDA
    
        // Find best device
        for (int dev = 0; dev < gpu.DeviceCount; ++dev)
        {
            cudaDeviceProp deviceProp;
            cudaGetDeviceProperties(&deviceProp, dev);
            if (deviceProp.major > gpu.ComputeCapabilityMajor)
            {
                gpu.ComputeCapabilityMajor = dev;
                gpu.ComputeCapabilityMajor = deviceProp.major;
                gpu.ComputeCapabilityMinor = 0;
            }
            if (deviceProp.minor > gpu.ComputeCapabilityMinor)
            {
                gpu.ComputeCapabilityMajor = dev;
                gpu.ComputeCapabilityMinor = deviceProp.minor;
            }
        }
        return gpu;
    }
    

    【讨论】:

    • 感谢分享。然而,这需要 CUDA 运行时在编译时可用,这是我最初想要避免的。然而,如今 CUDA 的使用范围更加广泛,因此最初的要求发生了变化,现在我们假设用户知道他们拥有 GPU,并且他们需要 CUDA 工具包才能使用它。
    猜你喜欢
    • 2015-09-10
    • 2021-09-28
    • 2011-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-20
    • 2018-05-22
    • 2020-12-04
    相关资源
    最近更新 更多