Выполнение GPU или CPU? - PullRequest
       8

Выполнение GPU или CPU?

1 голос
/ 15 мая 2019

Мне кажется, что должен быть способ написания кода, чтобы он мог работать как в CPU, так и в GPU.То есть я хочу написать что-то, что имеет (например) реализацию CPU FFT, которая может быть выполнена, если нет графического процессора, но по умолчанию используется GPFT FFT, когда присутствует графическая карта.Я не смог придумать правильный вопрос, чтобы веб-узлы могли предложить решение.

В моей цели приложения доступны графические процессоры.Мы хотим написать определенные функции для использования графических процессоров.Тем не менее, наши виртуальные машины разработки - это отдельная история.Кажется очень желательным иметь возможность запускать цикл кода / модульного тестирования, не переходя к аппаратному обеспечению графического процессора.

Если мне нужно выполнить некоторую умную проверку во время выполнения / загрузку библиотеки, я в порядке стот;Мне просто нужна поваренная книга.

Как люди непрерывно интегрируют код с поддержкой графического процессора?

Целевой средой является nVidia / CUDA.Я новичок в коде GPU, так что, возможно, это FAQ (но я его еще не нашел).

1 Ответ

3 голосов
/ 16 мая 2019

То, что я хочу, это время выполнения "у меня есть графический процессор?"переключиться, чтобы я мог выбрать один путь кода или другой

Я считаю, что это должно быть довольно просто.

Типичным подходом было бы:

  1. Статически связать ваш код с библиотекой CUDA Runtime library (cudart).Если вы компилируете с nvcc, это поведение по умолчанию.

  2. (предположительно) в начале вашего кода, выберите вызов API CUDA Runtime API, такой как cudaGetDevice().Используйте некоторую форму правильной проверки ошибок CUDA (всегда хорошая идея, в любом случае).В этом случае мы будем использовать ошибку, возвращаемую этим первым вызовом API среды выполнения, чтобы принять решение о пути (в отличие от простого завершения приложения).

  3. Если вызов API среды выполнения выполняется на шаге2 выше возвращает cudaSuccess (как функциональное возвращаемое значение, а не индекс устройства), так что можно с уверенностью предположить, что существует по крайней мере 1 функциональный графический процессор CUDA.В этом случае при желании / необходимости можно провести дополнительную проверку среды, возможно, следуя последовательности, аналогичной образцу кода CUDA deviceQuery.Этот статус может быть сохранен в вашей программе для принятия в будущем решения о путях кода.

  4. Если вызов 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 ваш друг.

...