pszilard
pszilard

Reputation: 1962

detecting NVIDIA GPUs without CUDA

I would like to extract a rather limited set of information about NVIDIA GPUs without linking against the CUDA libraries. The only information that is needed is compute capability and name of the GPU, more than this could be useful but it is not required. The code should be written in C (or C++). The information would be used at configure-time (when the CUDA toolkit is not available) and at run-time (when the executed binary is not compiled with CUDA support) to suggest the user that a supported GPU is present in the system.

As far as I understand, this is possible through the driver API, but I am not very familiar with the technical details of what this would require. So my questions are:

Note that the my first step would be to have some code for Linux, but ultimately I'd need platform-independent code. Considering the platform-availability of CUDA, for a complete solution this would involve code for on x86/AMD64 for Linux, Mac OS, and Windows (at least for now, the list could get soon extended with ARM).

Edit

What I meant by "it's possible through the driver API" is that one should be able to load libcuda.so dynamically and query the device properties through the driver API. I'm not sure about the details, though.

Upvotes: 7

Views: 5747

Answers (4)

Mike S.
Mike S.

Reputation: 118

I solved this problem by using and linking statically against the CUDA 6.0 SDK. It produces an application that works also well on a machines that does not have NVIDIA cards or on machines that the SDK is not installed. In such case it will indicate that there are zero CUDA capable devices.

There is an example in the samples included with the CUDA SDK calld deviceQuery - I used snippets from it to write the following code. I decide if a CUDA capable devices are present and if so which has the higest compute capabilities:

#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;
}

Upvotes: 1

Przemyslaw Zych
Przemyslaw Zych

Reputation: 2060

Unfortunately NVML doesn't provide information about device compute capability.

What you need to do is:

  1. Load CUDA library manually (application is not linked against libcuda)
    • If the library doesn't exist then CUDA driver is not installed
  2. Find pointers to necessary functions in the library
  3. Use driver API to query information about available GPUs

I hope this code will be helpful. I've tested it under Linux but with minor modifications it should also compile under 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; 
}

Test on 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

Test on linux with no CUDA driver

$ ./a.out
$ echo $?
1

Cheers

Upvotes: 10

lashgar
lashgar

Reputation: 5430

First, I think NVIDIA NVML is the API you are looking for. Second, there is an open-source project based on NVML called PAPI NVML.

Upvotes: 1

eLRuLL
eLRuLL

Reputation: 18799

Sure these people know the answer:

http://www.ozone3d.net/gpu_caps_viewer

but i can only know that i could be done with an installation of CUDA or OpenCL.

I think one way could be using OpenGL directly, maybe that is what you were talking about with the driver API, but i can only give you these example (CUDA required):

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

Upvotes: 1

Related Questions