Frage

Wir haben einige Nightly Build-Maschinen, die die CUDA-Bibliotheken installiert haben, aber das tun nicht haben eine CUDA-fähige GPU installiert. Diese Maschinen sind in der Lage CUDA-fähigen Programme bauen, aber sie sind nicht in der Lage, diese Programme ausgeführt werden.

In unserem automatisierten nächtlichen Build-Prozess, unsere Cmake Skripte verwenden, um den cmake Befehl

find_package(CUDA)

, um zu bestimmen, ob die CUDA-Software installiert ist. Dies setzt die cmake Variable CUDA_FOUND auf Plattformen, die CUDA-Software installiert haben. Das ist großartig und es funktioniert perfekt. Wenn CUDA_FOUND eingestellt wird, ist es OK, um Build-CUDA-fähigen Programme. Selbst wenn die Maschine keine CUDA-fähige GPU hat.

Aber CUDA-unter Verwendung von Testprogrammen scheitern natürlich auf den Nicht-GPU CUDA Maschinen, was unsere nächtlichen Dashboards aussehen „dirty“. Also ich Cmake wollen, dass diese Tests an solchen Maschinen zu vermeiden läuft. Aber ich möchte noch die CUDA-Software auf diesen Maschinen bauen.

Nach einem positiven CUDA_FOUND Ergebnis zu erzielen, würde ich auf das Vorhandensein eines tatsächlichen GPU Test mag, und dann eine Variable, sage CUDA_GPU_FOUND, reflektiert dies.

Was ist der einfachste Weg, cmake zu Test für das Vorhandensein eines CUDA-fähigen gpu zu bekommen?

Dies muss Arbeit auf drei Plattformen: Windows mit MSVC, Mac und Linux. (Deshalb haben wir cmake in erster Linie verwenden)

EDIT: Es gibt ein paar gut aussehende Vorschläge in den Antworten, wie auf das Vorhandensein eines GPU ein Programm Test schreiben. Was noch fehlt, ist das Mittel von CMake bekommt dieses Programm zur Konfigurationszeit zu kompilieren und auszuführen. Ich vermute, dass der TRY_RUN Befehl in CMake hier von entscheidender Bedeutung sein wird, aber leider, dass Kommando fast ohne Papiere , und ich kann nicht herausfinden, wie es funktioniert. Dieser CMake Teil des Problems könnte eine viel schwierigere Frage. Vielleicht sollte ich diese als zwei getrennte Fragen gestellt haben ...

War es hilfreich?

Lösung

Die Antwort auf diese Frage besteht aus zwei Teilen:

  1. Ein Programm, das Vorhandensein einer CUDA-fähige GPU zu erkennen.
  2. CMake-Code zu kompilieren, läuft, und interpretieren Sie das Ergebnis dieses Programms bei der Konfiguration.

Für Teil 1, die gpu Schnüffelprogramm, begann ich mit der Antwort von fabrizioM zur Verfügung gestellt, weil es so kompakt ist. Ich entdeckte schnell, dass ich viele Details benötigt in unbekannten Antwort gefunden, um es gut zu funktionieren. Was ich am Ende mit ist die folgende C-Quelldatei, die ich has_cuda_gpu.c benannt:

#include <stdio.h>
#include <cuda_runtime.h>

int main() {
    int deviceCount, device;
    int gpuDeviceCount = 0;
    struct cudaDeviceProp properties;
    cudaError_t cudaResultCode = cudaGetDeviceCount(&deviceCount);
    if (cudaResultCode != cudaSuccess) 
        deviceCount = 0;
    /* machines with no GPUs can still report one emulation device */
    for (device = 0; device < deviceCount; ++device) {
        cudaGetDeviceProperties(&properties, device);
        if (properties.major != 9999) /* 9999 means emulation only */
            ++gpuDeviceCount;
    }
    printf("%d GPU CUDA device(s) found\n", gpuDeviceCount);

    /* don't just return the number of gpus, because other runtime cuda
       errors can also yield non-zero return values */
    if (gpuDeviceCount > 0)
        return 0; /* success */
    else
        return 1; /* failure */
}

Beachten Sie, dass der Rückkehrcode Null in dem Fall, in dem eine CUDA-fähigen GPU gefunden wird. Dies liegt daran, auf einem meiner hat-CUDA-aber-nicht-GPU-Maschinen, ist dieses Programm einen Fehler Laufzeit generiert mit Nicht-Null-Exit-Code. So dass jeder Nicht-Null-Exit-Code wird interpretiert als „CUDA auf dieser Maschine nicht funktioniert“.

Sie fragen sich vielleicht, warum ich nicht CUDA-Emulationsmodus auf Nicht-GPU-Maschinen verwenden. Es ist, weil Emulationsmodus Buggy ist. Ich will nur meinen Code debuggen, und die Arbeit, um Fehler in CUDA GPU-Code. Ich habe keine Zeit, um den Emulator zu debuggen.

Der zweite Teil des Problems ist der cmake Code, um dieses Testprogramm zu verwenden. Nach einigem Kampf habe ich es herausgefunden. Der folgende Block ist Teil einer größeren CMakeLists.txt-Datei:

find_package(CUDA)
if(CUDA_FOUND)
    try_run(RUN_RESULT_VAR COMPILE_RESULT_VAR
        ${CMAKE_BINARY_DIR} 
        ${CMAKE_CURRENT_SOURCE_DIR}/has_cuda_gpu.c
        CMAKE_FLAGS 
            -DINCLUDE_DIRECTORIES:STRING=${CUDA_TOOLKIT_INCLUDE}
            -DLINK_LIBRARIES:STRING=${CUDA_CUDART_LIBRARY}
        COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT_VAR
        RUN_OUTPUT_VARIABLE RUN_OUTPUT_VAR)
    message("${RUN_OUTPUT_VAR}") # Display number of GPUs found
    # COMPILE_RESULT_VAR is TRUE when compile succeeds
    # RUN_RESULT_VAR is zero when a GPU is found
    if(COMPILE_RESULT_VAR AND NOT RUN_RESULT_VAR)
        set(CUDA_HAVE_GPU TRUE CACHE BOOL "Whether CUDA-capable GPU is present")
    else()
        set(CUDA_HAVE_GPU FALSE CACHE BOOL "Whether CUDA-capable GPU is present")
    endif()
endif(CUDA_FOUND)

Dies stellt ein CUDA_HAVE_GPU boolean Variable in cmake, die später verwendet werden können, bedingte Operationen auslösen.

Es dauerte eine lange Zeit, um herauszufinden, dass die umfassen und Verbindungsparameter müssen in der CMAKE_FLAGS Strophe gehen, und was die Syntax sein sollte. Die try_run Dokumentation ist sehr leicht, aber es gibt mehr Informationen in der try_compile Dokumentation , die ein eng verwandter Befehl. Ich brauchte noch die Bahn für Beispiele von try_compile scheuern und try_run bevor diese an der Arbeit.

Ein weiteres schwierig, aber wichtiges Detail ist das dritte Argument try_run, die „bindir“. Sie sollten wahrscheinlich setzen Sie diese immer auf ${CMAKE_BINARY_DIR}. Insbesondere stelle sie nicht auf ${CMAKE_CURRENT_BINARY_DIR}, wenn Sie in einem Unterverzeichnis Ihres Projekts sind. CMake erwartet das Unterverzeichnis CMakeFiles/CMakeTmp innerhalb bindir und Austriebe Fehler zu finden, wenn das Verzeichnis nicht existiert. Verwenden Sie einfach ${CMAKE_BINARY_DIR}, das ist ein Ort, an dem diese Unterverzeichnisse scheinen natürlich befinden.

Andere Tipps

Schreiben Sie ein einfaches Programm wie

#include<cuda.h>

int main (){
    int deviceCount;
    cudaError_t e = cudaGetDeviceCount(&deviceCount);
    return e == cudaSuccess ? deviceCount : -1;
}

und überprüfen Sie den Rückgabewert.

Ich schrieb nur einen reinen Python-Skript, das einige der Dinge tut, die Sie brauchen scheinen (ich viel davon aus dem pystream Projekt nahm). Es ist im Grunde nur ein Wrapper für einige Funktionen in der CUDA Laufzeitbibliothek (es verwendet ctypes). Schauen Sie sich die Funktion main () Beispiel für die Verwendung zu sehen. Beachten Sie auch, dass ich es gerade geschrieben haben, so ist es wahrscheinlich, Fehler enthalten. Verwenden Sie mit Vorsicht zu genießen.

#!/bin/bash

import sys
import platform
import ctypes

"""
cudart.py: used to access pars of the CUDA runtime library.
Most of this code was lifted from the pystream project (it's BSD licensed):
http://code.google.com/p/pystream

Note that this is likely to only work with CUDA 2.3
To extend to other versions, you may need to edit the DeviceProp Class
"""

cudaSuccess = 0
errorDict = {
    1: 'MissingConfigurationError',
    2: 'MemoryAllocationError',
    3: 'InitializationError',
    4: 'LaunchFailureError',
    5: 'PriorLaunchFailureError',
    6: 'LaunchTimeoutError',
    7: 'LaunchOutOfResourcesError',
    8: 'InvalidDeviceFunctionError',
    9: 'InvalidConfigurationError',
    10: 'InvalidDeviceError',
    11: 'InvalidValueError',
    12: 'InvalidPitchValueError',
    13: 'InvalidSymbolError',
    14: 'MapBufferObjectFailedError',
    15: 'UnmapBufferObjectFailedError',
    16: 'InvalidHostPointerError',
    17: 'InvalidDevicePointerError',
    18: 'InvalidTextureError',
    19: 'InvalidTextureBindingError',
    20: 'InvalidChannelDescriptorError',
    21: 'InvalidMemcpyDirectionError',
    22: 'AddressOfConstantError',
    23: 'TextureFetchFailedError',
    24: 'TextureNotBoundError',
    25: 'SynchronizationError',
    26: 'InvalidFilterSettingError',
    27: 'InvalidNormSettingError',
    28: 'MixedDeviceExecutionError',
    29: 'CudartUnloadingError',
    30: 'UnknownError',
    31: 'NotYetImplementedError',
    32: 'MemoryValueTooLargeError',
    33: 'InvalidResourceHandleError',
    34: 'NotReadyError',
    0x7f: 'StartupFailureError',
    10000: 'ApiFailureBaseError'}


try:
    if platform.system() == "Microsoft":
        _libcudart = ctypes.windll.LoadLibrary('cudart.dll')
    elif platform.system()=="Darwin":
        _libcudart = ctypes.cdll.LoadLibrary('libcudart.dylib')
    else:
        _libcudart = ctypes.cdll.LoadLibrary('libcudart.so')
    _libcudart_error = None
except OSError, e:
    _libcudart_error = e
    _libcudart = None

def _checkCudaStatus(status):
    if status != cudaSuccess:
        eClassString = errorDict[status]
        # Get the class by name from the top level of this module
        eClass = globals()[eClassString]
        raise eClass()

def _checkDeviceNumber(device):
    assert isinstance(device, int), "device number must be an int"
    assert device >= 0, "device number must be greater than 0"
    assert device < 2**8-1, "device number must be < 255"


# cudaDeviceProp
class DeviceProp(ctypes.Structure):
    _fields_ = [
         ("name", 256*ctypes.c_char), #  < ASCII string identifying device
         ("totalGlobalMem", ctypes.c_size_t), #  < Global memory available on device in bytes
         ("sharedMemPerBlock", ctypes.c_size_t), #  < Shared memory available per block in bytes
         ("regsPerBlock", ctypes.c_int), #  < 32-bit registers available per block
         ("warpSize", ctypes.c_int), #  < Warp size in threads
         ("memPitch", ctypes.c_size_t), #  < Maximum pitch in bytes allowed by memory copies
         ("maxThreadsPerBlock", ctypes.c_int), #  < Maximum number of threads per block
         ("maxThreadsDim", 3*ctypes.c_int), #  < Maximum size of each dimension of a block
         ("maxGridSize", 3*ctypes.c_int), #  < Maximum size of each dimension of a grid
         ("clockRate", ctypes.c_int), #  < Clock frequency in kilohertz
         ("totalConstMem", ctypes.c_size_t), #  < Constant memory available on device in bytes
         ("major", ctypes.c_int), #  < Major compute capability
         ("minor", ctypes.c_int), #  < Minor compute capability
         ("textureAlignment", ctypes.c_size_t), #  < Alignment requirement for textures
         ("deviceOverlap", ctypes.c_int), #  < Device can concurrently copy memory and execute a kernel
         ("multiProcessorCount", ctypes.c_int), #  < Number of multiprocessors on device
         ("kernelExecTimeoutEnabled", ctypes.c_int), #  < Specified whether there is a run time limit on kernels
         ("integrated", ctypes.c_int), #  < Device is integrated as opposed to discrete
         ("canMapHostMemory", ctypes.c_int), #  < Device can map host memory with cudaHostAlloc/cudaHostGetDevicePointer
         ("computeMode", ctypes.c_int), #  < Compute mode (See ::cudaComputeMode)
         ("__cudaReserved", 36*ctypes.c_int),
]

    def __str__(self):
        return """NVidia GPU Specifications:
    Name: %s
    Total global mem: %i
    Shared mem per block: %i
    Registers per block: %i
    Warp size: %i
    Mem pitch: %i
    Max threads per block: %i
    Max treads dim: (%i, %i, %i)
    Max grid size: (%i, %i, %i)
    Total const mem: %i
    Compute capability: %i.%i
    Clock Rate (GHz): %f
    Texture alignment: %i
""" % (self.name, self.totalGlobalMem, self.sharedMemPerBlock,
       self.regsPerBlock, self.warpSize, self.memPitch,
       self.maxThreadsPerBlock,
       self.maxThreadsDim[0], self.maxThreadsDim[1], self.maxThreadsDim[2],
       self.maxGridSize[0], self.maxGridSize[1], self.maxGridSize[2],
       self.totalConstMem, self.major, self.minor,
       float(self.clockRate)/1.0e6, self.textureAlignment)

def cudaGetDeviceCount():
    if _libcudart is None: return  0
    deviceCount = ctypes.c_int()
    status = _libcudart.cudaGetDeviceCount(ctypes.byref(deviceCount))
    _checkCudaStatus(status)
    return deviceCount.value

def getDeviceProperties(device):
    if _libcudart is None: return  None
    _checkDeviceNumber(device)
    props = DeviceProp()
    status = _libcudart.cudaGetDeviceProperties(ctypes.byref(props), device)
    _checkCudaStatus(status)
    return props

def getDriverVersion():
    if _libcudart is None: return  None
    version = ctypes.c_int()
    _libcudart.cudaDriverGetVersion(ctypes.byref(version))
    v = "%d.%d" % (version.value//1000,
                   version.value%100)
    return v

def getRuntimeVersion():
    if _libcudart is None: return  None
    version = ctypes.c_int()
    _libcudart.cudaRuntimeGetVersion(ctypes.byref(version))
    v = "%d.%d" % (version.value//1000,
                   version.value%100)
    return v

def getGpuCount():
    count=0
    for ii in range(cudaGetDeviceCount()):
        props = getDeviceProperties(ii)
        if props.major!=9999: count+=1
    return count

def getLoadError():
    return _libcudart_error


version = getDriverVersion()
if version is not None and not version.startswith('2.3'):
    sys.stdout.write("WARNING: Driver version %s may not work with %s\n" %
                     (version, sys.argv[0]))

version = getRuntimeVersion()
if version is not None and not version.startswith('2.3'):
    sys.stdout.write("WARNING: Runtime version %s may not work with %s\n" %
                     (version, sys.argv[0]))


def main():

    sys.stdout.write("Driver version: %s\n" % getDriverVersion())
    sys.stdout.write("Runtime version: %s\n" % getRuntimeVersion())

    nn = cudaGetDeviceCount()
    sys.stdout.write("Device count: %s\n" % nn)

    for ii in range(nn):
        props = getDeviceProperties(ii)
        sys.stdout.write("\nDevice %d:\n" % ii)
        #sys.stdout.write("%s" % props)
        for f_name, f_type in props._fields_:
            attr = props.__getattribute__(f_name)
            sys.stdout.write( "  %s: %s\n" % (f_name, attr))

    gpuCount = getGpuCount()
    if gpuCount > 0:
        sys.stdout.write("\n")
    sys.stdout.write("GPU count: %d\n" % getGpuCount())
    e = getLoadError()
    if e is not None:
        sys.stdout.write("There was an error loading a library:\n%s\n\n" % e)

if __name__=="__main__":
    main()

Sie können kleine GPU Abfrage Programm kompilieren, wenn CUDA gefunden wurde. hier ist einfach Sie die Bedürfnisse annehmen kann:

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

int main(int argc, char** argv) {
  int ct,dev;
  cudaError_t code;
  struct cudaDeviceProp prop;

 cudaGetDeviceCount(&ct);
 code = cudaGetLastError();
 if(code)  printf("%s\n", cudaGetErrorString(code));


if(ct == 0) {
   printf("Cuda device not found.\n");
   exit(0);
}
 printf("Found %i Cuda device(s).\n",ct);

for (dev = 0; dev < ct; ++dev) {
printf("Cuda device %i\n", dev);

cudaGetDeviceProperties(&prop,dev);
printf("\tname : %s\n", prop.name);
 printf("\ttotalGlobablMem: %lu\n", (unsigned long)prop.totalGlobalMem);
printf("\tsharedMemPerBlock: %i\n", prop.sharedMemPerBlock);
printf("\tregsPerBlock: %i\n", prop.regsPerBlock);
printf("\twarpSize: %i\n", prop.warpSize);
printf("\tmemPitch: %i\n", prop.memPitch);
printf("\tmaxThreadsPerBlock: %i\n", prop.maxThreadsPerBlock);
printf("\tmaxThreadsDim: %i, %i, %i\n", prop.maxThreadsDim[0], prop.maxThreadsDim[1], prop.maxThreadsDim[2]);
printf("\tmaxGridSize: %i, %i, %i\n", prop.maxGridSize[0], prop.maxGridSize[1], prop.maxGridSize[2]);
printf("\tclockRate: %i\n", prop.clockRate);
printf("\ttotalConstMem: %i\n", prop.totalConstMem);
printf("\tmajor: %i\n", prop.major);
printf("\tminor: %i\n", prop.minor);
printf("\ttextureAlignment: %i\n", prop.textureAlignment);
printf("\tdeviceOverlap: %i\n", prop.deviceOverlap);
printf("\tmultiProcessorCount: %i\n", prop.multiProcessorCount);
}
}

Ein nützlicher Ansatz ist, um Programme auszuführen, dass CUDA installiert hat, wie nvidia-smi, um zu sehen, was sie zurückkehren.

        find_program(_nvidia_smi "nvidia-smi")
        if (_nvidia_smi)
            set(DETECT_GPU_COUNT_NVIDIA_SMI 0)
            # execute nvidia-smi -L to get a short list of GPUs available
            exec_program(${_nvidia_smi_path} ARGS -L
                OUTPUT_VARIABLE _nvidia_smi_out
                RETURN_VALUE    _nvidia_smi_ret)
            # process the stdout of nvidia-smi
            if (_nvidia_smi_ret EQUAL 0)
                # convert string with newlines to list of strings
                string(REGEX REPLACE "\n" ";" _nvidia_smi_out "${_nvidia_smi_out}")
                foreach(_line ${_nvidia_smi_out})
                    if (_line MATCHES "^GPU [0-9]+:")
                        math(EXPR DETECT_GPU_COUNT_NVIDIA_SMI "${DETECT_GPU_COUNT_NVIDIA_SMI}+1")
                        # the UUID is not very useful for the user, remove it
                        string(REGEX REPLACE " \\(UUID:.*\\)" "" _gpu_info "${_line}")
                        if (NOT _gpu_info STREQUAL "")
                            list(APPEND DETECT_GPU_INFO "${_gpu_info}")
                        endif()
                    endif()
                endforeach()

                check_num_gpu_info(${DETECT_GPU_COUNT_NVIDIA_SMI} DETECT_GPU_INFO)
                set(DETECT_GPU_COUNT ${DETECT_GPU_COUNT_NVIDIA_SMI})
            endif()
        endif()

Man könnte auch Abfrage linux / proc oder lspci. Siehe voll gearbeitet CMake Beispiel unter https://github.com/gromacs/ gromacs / Blob / Master / cmake / gmxDetectGpu.cmake

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top