mirror of https://github.com/gnss-sdr/gnss-sdr
518 lines
16 KiB
C++
518 lines
16 KiB
C++
/**
|
|
* Copyright 1993-2013 NVIDIA Corporation. All rights reserved.
|
|
*
|
|
* Please refer to the NVIDIA end user license agreement (EULA) associated
|
|
* with this source code for terms and conditions that govern your use of
|
|
* this software. Any use, reproduction, disclosure, or distribution of
|
|
* this software and related documentation outside the terms of the EULA
|
|
* is strictly prohibited.
|
|
*
|
|
*/
|
|
|
|
// Helper functions for CUDA Driver API error handling (make sure that CUDA_H is included in your projects)
|
|
#ifndef HELPER_CUDA_DRVAPI_H
|
|
#define HELPER_CUDA_DRVAPI_H
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <helper_string.h>
|
|
#include <drvapi_error_string.h>
|
|
|
|
#ifndef MAX
|
|
#define MAX(a,b) (a > b ? a : b)
|
|
#endif
|
|
|
|
#ifndef HELPER_CUDA_H
|
|
inline int ftoi(float value)
|
|
{
|
|
return (value >= 0 ? (int)(value + 0.5) : (int)(value - 0.5));
|
|
}
|
|
#endif
|
|
|
|
#ifndef EXIT_WAIVED
|
|
#define EXIT_WAIVED 2
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// These are CUDA Helper functions
|
|
|
|
// add a level of protection to the CUDA SDK samples, let's force samples to explicitly include CUDA.H
|
|
#ifdef __cuda_cuda_h__
|
|
// This will output the proper CUDA error strings in the event that a CUDA host call returns an error
|
|
#ifndef checkCudaErrors
|
|
#define checkCudaErrors(err) __checkCudaErrors (err, __FILE__, __LINE__)
|
|
|
|
// These are the inline versions for all of the SDK helper functions
|
|
inline void __checkCudaErrors(CUresult err, const char *file, const int line)
|
|
{
|
|
if (CUDA_SUCCESS != err)
|
|
{
|
|
fprintf(stderr, "checkCudaErrors() Driver API error = %04d \"%s\" from file <%s>, line %i.\n",
|
|
err, getCudaDrvErrorString(err), file, line);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef getLastCudaDrvErrorMsg
|
|
#undef getLastCudaDrvErrorMsg
|
|
#endif
|
|
|
|
#define getLastCudaDrvErrorMsg(msg) __getLastCudaDrvErrorMsg (msg, __FILE__, __LINE__)
|
|
|
|
inline void __getLastCudaDrvErrorMsg(const char *msg, const char *file, const int line)
|
|
{
|
|
CUresult err = cuCtxSynchronize();
|
|
|
|
if (CUDA_SUCCESS != err)
|
|
{
|
|
fprintf(stderr, "getLastCudaDrvErrorMsg -> %s", msg);
|
|
fprintf(stderr, "getLastCudaDrvErrorMsg -> cuCtxSynchronize API error = %04d \"%s\" in file <%s>, line %i.\n",
|
|
err, getCudaDrvErrorString(err), file, line);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
// This function wraps the CUDA Driver API into a template function
|
|
template <class T>
|
|
inline void getCudaAttribute(T *attribute, CUdevice_attribute device_attribute, int device)
|
|
{
|
|
CUresult error_result = cuDeviceGetAttribute(attribute, device_attribute, device);
|
|
|
|
if (error_result != CUDA_SUCCESS)
|
|
{
|
|
printf("cuDeviceGetAttribute returned %d\n-> %s\n", (int)error_result, getCudaDrvErrorString(error_result));
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Beginning of GPU Architecture definitions
|
|
inline int _ConvertSMVer2CoresDRV(int major, int minor)
|
|
{
|
|
// Defines for GPU Architecture types (using the SM version to determine the # of cores per SM
|
|
typedef struct
|
|
{
|
|
int SM; // 0xMm (hexidecimal notation), M = SM Major version, and m = SM minor version
|
|
int Cores;
|
|
} sSMtoCores;
|
|
|
|
sSMtoCores nGpuArchCoresPerSM[] =
|
|
{
|
|
{ 0x20, 32 }, // Fermi Generation (SM 2.0) GF100 class
|
|
{ 0x21, 48 }, // Fermi Generation (SM 2.1) GF10x class
|
|
{ 0x30, 192}, // Kepler Generation (SM 3.0) GK10x class
|
|
{ 0x32, 192}, // Kepler Generation (SM 3.2) GK10x class
|
|
{ 0x35, 192}, // Kepler Generation (SM 3.5) GK11x class
|
|
{ 0x37, 192}, // Kepler Generation (SM 3.7) GK21x class
|
|
{ 0x50, 128}, // Maxwell Generation (SM 5.0) GM10x class
|
|
{ 0x52, 128}, // Maxwell Generation (SM 5.2) GM20x class
|
|
{ -1, -1 }
|
|
};
|
|
|
|
int index = 0;
|
|
|
|
while (nGpuArchCoresPerSM[index].SM != -1)
|
|
{
|
|
if (nGpuArchCoresPerSM[index].SM == ((major << 4) + minor))
|
|
{
|
|
return nGpuArchCoresPerSM[index].Cores;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
|
|
// If we don't find the values, we default use the previous one to run properly
|
|
printf("MapSMtoCores for SM %d.%d is undefined. Default to use %d Cores/SM\n", major, minor, nGpuArchCoresPerSM[index-1].Cores);
|
|
return nGpuArchCoresPerSM[index-1].Cores;
|
|
}
|
|
// end of GPU Architecture definitions
|
|
|
|
#ifdef __cuda_cuda_h__
|
|
// General GPU Device CUDA Initialization
|
|
inline int gpuDeviceInitDRV(int ARGC, const char **ARGV)
|
|
{
|
|
int cuDevice = 0;
|
|
int deviceCount = 0;
|
|
CUresult err = cuInit(0);
|
|
|
|
if (CUDA_SUCCESS == err)
|
|
{
|
|
checkCudaErrors(cuDeviceGetCount(&deviceCount));
|
|
}
|
|
|
|
if (deviceCount == 0)
|
|
{
|
|
fprintf(stderr, "cudaDeviceInit error: no devices supporting CUDA\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
int dev = 0;
|
|
dev = getCmdLineArgumentInt(ARGC, (const char **) ARGV, "device=");
|
|
|
|
if (dev < 0)
|
|
{
|
|
dev = 0;
|
|
}
|
|
|
|
if (dev > deviceCount-1)
|
|
{
|
|
fprintf(stderr, "\n");
|
|
fprintf(stderr, ">> %d CUDA capable GPU device(s) detected. <<\n", deviceCount);
|
|
fprintf(stderr, ">> cudaDeviceInit (-device=%d) is not a valid GPU device. <<\n", dev);
|
|
fprintf(stderr, "\n");
|
|
return -dev;
|
|
}
|
|
|
|
checkCudaErrors(cuDeviceGet(&cuDevice, dev));
|
|
char name[100];
|
|
cuDeviceGetName(name, 100, cuDevice);
|
|
|
|
int computeMode;
|
|
getCudaAttribute<int>(&computeMode, CU_DEVICE_ATTRIBUTE_COMPUTE_MODE, dev);
|
|
|
|
if (computeMode == CU_COMPUTEMODE_PROHIBITED)
|
|
{
|
|
fprintf(stderr, "Error: device is running in <CU_COMPUTEMODE_PROHIBITED>, no threads can use this CUDA Device.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (checkCmdLineFlag(ARGC, (const char **) ARGV, "quiet") == false)
|
|
{
|
|
printf("gpuDeviceInitDRV() Using CUDA Device [%d]: %s\n", dev, name);
|
|
}
|
|
|
|
return dev;
|
|
}
|
|
|
|
// This function returns the best GPU based on performance
|
|
inline int gpuGetMaxGflopsDeviceIdDRV()
|
|
{
|
|
CUdevice current_device = 0;
|
|
CUdevice max_perf_device = 0;
|
|
int device_count = 0;
|
|
int sm_per_multiproc = 0;
|
|
unsigned long long max_compute_perf = 0;
|
|
int best_SM_arch = 0;
|
|
int major = 0;
|
|
int minor = 0;
|
|
int multiProcessorCount;
|
|
int clockRate;
|
|
int devices_prohibited = 0;
|
|
|
|
cuInit(0);
|
|
checkCudaErrors(cuDeviceGetCount(&device_count));
|
|
|
|
if (device_count == 0)
|
|
{
|
|
fprintf(stderr, "gpuGetMaxGflopsDeviceIdDRV error: no devices supporting CUDA\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Find the best major SM Architecture GPU device
|
|
while (current_device < device_count)
|
|
{
|
|
checkCudaErrors(cuDeviceComputeCapability(&major, &minor, current_device));
|
|
|
|
if (major > 0 && major < 9999)
|
|
{
|
|
best_SM_arch = MAX(best_SM_arch, major);
|
|
}
|
|
|
|
current_device++;
|
|
}
|
|
|
|
// Find the best CUDA capable GPU device
|
|
current_device = 0;
|
|
|
|
while (current_device < device_count)
|
|
{
|
|
checkCudaErrors(cuDeviceGetAttribute(&multiProcessorCount,
|
|
CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT,
|
|
current_device));
|
|
checkCudaErrors(cuDeviceGetAttribute(&clockRate,
|
|
CU_DEVICE_ATTRIBUTE_CLOCK_RATE,
|
|
current_device));
|
|
checkCudaErrors(cuDeviceComputeCapability(&major, &minor, current_device));
|
|
|
|
int computeMode;
|
|
getCudaAttribute<int>(&computeMode, CU_DEVICE_ATTRIBUTE_COMPUTE_MODE, current_device);
|
|
|
|
if (computeMode != CU_COMPUTEMODE_PROHIBITED)
|
|
{
|
|
if (major == 9999 && minor == 9999)
|
|
{
|
|
sm_per_multiproc = 1;
|
|
}
|
|
else
|
|
{
|
|
sm_per_multiproc = _ConvertSMVer2CoresDRV(major, minor);
|
|
}
|
|
|
|
unsigned long long compute_perf = (unsigned long long) (multiProcessorCount * sm_per_multiproc * clockRate);
|
|
|
|
if (compute_perf > max_compute_perf)
|
|
{
|
|
// If we find GPU with SM major > 2, search only these
|
|
if (best_SM_arch > 2)
|
|
{
|
|
// If our device==dest_SM_arch, choose this, or else pass
|
|
if (major == best_SM_arch)
|
|
{
|
|
max_compute_perf = compute_perf;
|
|
max_perf_device = current_device;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
max_compute_perf = compute_perf;
|
|
max_perf_device = current_device;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
devices_prohibited++;
|
|
}
|
|
|
|
++current_device;
|
|
}
|
|
|
|
if (devices_prohibited == device_count)
|
|
{
|
|
fprintf(stderr, "gpuGetMaxGflopsDeviceIdDRV error: all devices have compute mode prohibited.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return max_perf_device;
|
|
}
|
|
|
|
// This function returns the best Graphics GPU based on performance
|
|
inline int gpuGetMaxGflopsGLDeviceIdDRV()
|
|
{
|
|
CUdevice current_device = 0, max_perf_device = 0;
|
|
int device_count = 0, sm_per_multiproc = 0;
|
|
int max_compute_perf = 0, best_SM_arch = 0;
|
|
int major = 0, minor = 0, multiProcessorCount, clockRate;
|
|
int bTCC = 0;
|
|
int devices_prohibited = 0;
|
|
char deviceName[256];
|
|
|
|
cuInit(0);
|
|
checkCudaErrors(cuDeviceGetCount(&device_count));
|
|
|
|
if (device_count == 0)
|
|
{
|
|
fprintf(stderr, "gpuGetMaxGflopsGLDeviceIdDRV error: no devices supporting CUDA\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Find the best major SM Architecture GPU device that are graphics devices
|
|
while (current_device < device_count)
|
|
{
|
|
checkCudaErrors(cuDeviceGetName(deviceName, 256, current_device));
|
|
checkCudaErrors(cuDeviceComputeCapability(&major, &minor, current_device));
|
|
|
|
#if CUDA_VERSION >= 3020
|
|
checkCudaErrors(cuDeviceGetAttribute(&bTCC, CU_DEVICE_ATTRIBUTE_TCC_DRIVER, current_device));
|
|
#else
|
|
|
|
// Assume a Tesla GPU is running in TCC if we are running CUDA 3.1
|
|
if (deviceName[0] == 'T')
|
|
{
|
|
bTCC = 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
int computeMode;
|
|
getCudaAttribute<int>(&computeMode, CU_DEVICE_ATTRIBUTE_COMPUTE_MODE, current_device);
|
|
|
|
if (computeMode != CU_COMPUTEMODE_PROHIBITED)
|
|
{
|
|
if (!bTCC)
|
|
{
|
|
if (major > 0 && major < 9999)
|
|
{
|
|
best_SM_arch = MAX(best_SM_arch, major);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
devices_prohibited++;
|
|
}
|
|
|
|
current_device++;
|
|
}
|
|
|
|
if (devices_prohibited == device_count)
|
|
{
|
|
fprintf(stderr, "gpuGetMaxGflopsGLDeviceIdDRV error: all devices have compute mode prohibited.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
// Find the best CUDA capable GPU device
|
|
current_device = 0;
|
|
|
|
while (current_device < device_count)
|
|
{
|
|
checkCudaErrors(cuDeviceGetAttribute(&multiProcessorCount,
|
|
CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT,
|
|
current_device));
|
|
checkCudaErrors(cuDeviceGetAttribute(&clockRate,
|
|
CU_DEVICE_ATTRIBUTE_CLOCK_RATE,
|
|
current_device));
|
|
checkCudaErrors(cuDeviceComputeCapability(&major, &minor, current_device));
|
|
|
|
#if CUDA_VERSION >= 3020
|
|
checkCudaErrors(cuDeviceGetAttribute(&bTCC, CU_DEVICE_ATTRIBUTE_TCC_DRIVER, current_device));
|
|
#else
|
|
|
|
// Assume a Tesla GPU is running in TCC if we are running CUDA 3.1
|
|
if (deviceName[0] == 'T')
|
|
{
|
|
bTCC = 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
int computeMode;
|
|
getCudaAttribute<int>(&computeMode, CU_DEVICE_ATTRIBUTE_COMPUTE_MODE, current_device);
|
|
|
|
if (computeMode != CU_COMPUTEMODE_PROHIBITED)
|
|
{
|
|
if (major == 9999 && minor == 9999)
|
|
{
|
|
sm_per_multiproc = 1;
|
|
}
|
|
else
|
|
{
|
|
sm_per_multiproc = _ConvertSMVer2CoresDRV(major, minor);
|
|
}
|
|
|
|
// If this is a Tesla based GPU and SM 2.0, and TCC is disabled, this is a contendor
|
|
if (!bTCC) // Is this GPU running the TCC driver? If so we pass on this
|
|
{
|
|
int compute_perf = multiProcessorCount * sm_per_multiproc * clockRate;
|
|
|
|
if (compute_perf > max_compute_perf)
|
|
{
|
|
// If we find GPU with SM major > 2, search only these
|
|
if (best_SM_arch > 2)
|
|
{
|
|
// If our device = dest_SM_arch, then we pick this one
|
|
if (major == best_SM_arch)
|
|
{
|
|
max_compute_perf = compute_perf;
|
|
max_perf_device = current_device;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
max_compute_perf = compute_perf;
|
|
max_perf_device = current_device;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
++current_device;
|
|
}
|
|
|
|
return max_perf_device;
|
|
}
|
|
|
|
// General initialization call to pick the best CUDA Device
|
|
inline CUdevice findCudaDeviceDRV(int argc, const char **argv)
|
|
{
|
|
CUdevice cuDevice;
|
|
int devID = 0;
|
|
|
|
// If the command-line has a device number specified, use it
|
|
if (checkCmdLineFlag(argc, (const char **)argv, "device"))
|
|
{
|
|
devID = gpuDeviceInitDRV(argc, argv);
|
|
|
|
if (devID < 0)
|
|
{
|
|
printf("exiting...\n");
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Otherwise pick the device with highest Gflops/s
|
|
char name[100];
|
|
devID = gpuGetMaxGflopsDeviceIdDRV();
|
|
checkCudaErrors(cuDeviceGet(&cuDevice, devID));
|
|
cuDeviceGetName(name, 100, cuDevice);
|
|
printf("> Using CUDA Device [%d]: %s\n", devID, name);
|
|
}
|
|
|
|
cuDeviceGet(&cuDevice, devID);
|
|
|
|
return cuDevice;
|
|
}
|
|
|
|
// This function will pick the best CUDA device available with OpenGL interop
|
|
inline CUdevice findCudaGLDeviceDRV(int argc, const char **argv)
|
|
{
|
|
CUdevice cuDevice;
|
|
int devID = 0;
|
|
|
|
// If the command-line has a device number specified, use it
|
|
if (checkCmdLineFlag(argc, (const char **)argv, "device"))
|
|
{
|
|
devID = gpuDeviceInitDRV(argc, (const char **)argv);
|
|
|
|
if (devID < 0)
|
|
{
|
|
printf("no CUDA capable devices found, exiting...\n");
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char name[100];
|
|
// Otherwise pick the device with highest Gflops/s
|
|
devID = gpuGetMaxGflopsGLDeviceIdDRV();
|
|
checkCudaErrors(cuDeviceGet(&cuDevice, devID));
|
|
cuDeviceGetName(name, 100, cuDevice);
|
|
printf("> Using CUDA/GL Device [%d]: %s\n", devID, name);
|
|
}
|
|
|
|
return devID;
|
|
}
|
|
|
|
// General check for CUDA GPU SM Capabilities
|
|
inline bool checkCudaCapabilitiesDRV(int major_version, int minor_version, int devID)
|
|
{
|
|
CUdevice cuDevice;
|
|
char name[256];
|
|
int major = 0, minor = 0;
|
|
|
|
checkCudaErrors(cuDeviceGet(&cuDevice, devID));
|
|
checkCudaErrors(cuDeviceGetName(name, 100, cuDevice));
|
|
checkCudaErrors(cuDeviceComputeCapability(&major, &minor, devID));
|
|
|
|
if ((major > major_version) ||
|
|
(major == major_version && minor >= minor_version))
|
|
{
|
|
printf("> Device %d: <%16s >, Compute SM %d.%d detected\n", devID, name, major, minor);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
printf("No GPU device was found that can support CUDA compute capability %d.%d.\n", major_version, minor_version);
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// end of CUDA Helper Functions
|
|
|
|
#endif
|