То, что я хочу, это время выполнения "у меня есть графический процессор?"переключиться, чтобы я мог выбрать один путь кода или другой
Я считаю, что это должно быть довольно просто.
Типичным подходом было бы:
Статически связать ваш код с библиотекой CUDA Runtime library (cudart).Если вы компилируете с nvcc
, это поведение по умолчанию.
(предположительно) в начале вашего кода, выберите вызов API CUDA Runtime API, такой как cudaGetDevice()
.Используйте некоторую форму правильной проверки ошибок CUDA (всегда хорошая идея, в любом случае).В этом случае мы будем использовать ошибку, возвращаемую этим первым вызовом API среды выполнения, чтобы принять решение о пути (в отличие от простого завершения приложения).
Если вызов API среды выполнения выполняется на шаге2 выше возвращает cudaSuccess
(как функциональное возвращаемое значение, а не индекс устройства), так что можно с уверенностью предположить, что существует по крайней мере 1 функциональный графический процессор CUDA.В этом случае при желании / необходимости можно провести дополнительную проверку среды, возможно, следуя последовательности, аналогичной образцу кода CUDA deviceQuery
.Этот статус может быть сохранен в вашей программе для принятия в будущем решения о путях кода.
Если вызов API времени выполнения на шаге 2 возвращает что-то кроме cudaSuccess
, это почти наверняка означаетчто CUDA не работает, возможно, потому что нет CUDA GPU.В этом случае, я бы посоветовал против любого дальнейшего использования любого API или библиотеки CUDA, и с этого момента ваш код должен использовать пути кода только для хоста.
Вот полностью проработанныйпример.Он использует библиотеку CUFFT для выполнения простой операции FFT, если найдена функциональная среда CUDA.В противном случае он использует FFTW, чтобы сделать то же самое в коде хоста.Обратите внимание, что в дополнение к статическим связям с библиотекой cudart (по умолчанию nvcc
, поэтому не очевидно), я также статически связываюсь с библиотекой CUFFT.По крайней мере, в Linux, как в приведенном здесь примере, это предотвращает сбои во время запуска приложения из-за невозможности найти динамические библиотеки, на которые можно ссылаться (что вообще может помешать запуску приложения; тогда как в этом случае наша цель состоит в том, чтобы приложениезапускается, но выбирает пути к коду хоста).
$ cat t467.cu
#include <cufft.h>
#include <fftw.h>
#include <iostream>
int main(){
double data[] = {0.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, -1.0f};
int N = sizeof(data)/sizeof(data[0]);
int dev = 0;
if (cudaGetDevice(&dev) == cudaSuccess) {
// GPU code path
cufftDoubleComplex *din, *dout, *in, *out;
in = new cufftDoubleComplex[N];
out = new cufftDoubleComplex[N];
for (int i = 0; i < N; i++) in[i].x = data[i];
cudaError_t err = cudaMalloc(&din, sizeof(din[0]) * N);
err = cudaMalloc(&dout, sizeof(din[0]) * N);
cufftHandle plan;
cufftResult cstat = cufftPlan1d(&plan, N, CUFFT_Z2Z, 1);
cudaMemcpy(din, in, N*sizeof(din[0]), cudaMemcpyHostToDevice);
cstat = cufftExecZ2Z(plan, din, dout, CUFFT_FORWARD);
cudaMemcpy(out, dout, N*sizeof(din[0]), cudaMemcpyDeviceToHost);
for (int i = 0; i < N; i++) data[i] = out[i].x * out[i].x + out[i].y * out[i].y;
cudaFree(din); cudaFree(dout);
delete[] in; delete[] out;
cufftDestroy(plan);
std::cout << "GPU calculation: " << std::endl;
}
else {
// CPU code path
fftw_complex *in, *out;
fftw_plan p;
in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);
for (int i = 0; i < N; i++) {in[i].re= data[i]; in[i].im = 0;}
p = fftw_create_plan(N, FFTW_FORWARD, FFTW_ESTIMATE);
fftw_one(p, in, out);
fftw_destroy_plan(p);
for (int i = 0; i < N; i++) data[i] = out[i].re * out[i].re + out[i].im * out[i].im;
fftw_free(in); fftw_free(out);
std::cout << "CPU calculation: " << std::endl;
}
for (int i = 0; i < N; i++)
std::cout << data[i] << ", ";
std::cout << std::endl;
return 0;
}
$ nvcc t467.cu -o t467 -lcufft_static -lculibos -lfftw -lm
$ ./t467
GPU calculation:
0, 0, 16, 0, 0, 0, 16, 0,
$ CUDA_VISIBLE_DEVICES="" ./t467
CPU calculation:
0, 0, 16, 0, 0, 0, 16, 0,
$
Обратите внимание, что приведенный выше пример по-прежнему динамически связывается с fftw, поэтому вашей среде выполнения (как CPU, так и GPU) должна быть доступна соответствующая библиотека fftwX.so.Общий процесс того, как заставить исполняемый файл linux работать во множестве параметров (вне зависимостей CUDA), выходит за рамки этого примера или того, на что я собираюсь ответить.В Linux ldd
ваш друг.