2015-12-22 4 views
0

Приложение с открытым исходным кодом C++/Qt, которое меня интересует, зависит от CUDA. Мой macbook pro (середина 2014 года) имеет запас Intel Iris Pro и графическую карту NVidia. Естественно, готовое приложение не будет работать.удалить зависимость CUDA?

Я нашел этот эмулятор: https://github.com/gtcasl/gpuocelot - но он протестирован только на Linux, и есть несколько открытых проблем, которые он не компилирует на Mac.

У меня есть источник - могу ли я заменить зависимость CUDA на эквиваленты C++ за счет более медленной обработки? Я надеюсь, что-то вроде

  1. расширений переименования файлов: .cu в .cpp
  2. ссылки удалить CUDA из макияжа файла
  3. заменить заголовки CUDA с эквивалентными C++ станда Lib заголовков
  4. регулируют Makefile, добавив, отсутствуют ссылки библиотеки при необходимости
  5. исправления оставшихся без вести вызовов функций (надеюсь, только один или два) с C++ кода (возможно, списан из Ocelot)

Но я боюсь, что все не так просто. Прежде чем начать, я хотел бы проверить здравый смысл.

+4

Это не так просто. Или разумно. – talonmies

+8

Я сомневаюсь, что это будет * просто *. Этот вопрос, по сути, является обратным: «Как переносить этот код в CUDA?» Вы должны быть немного осведомлены в CUDA, чтобы выполнить это - больше знаний, чем вы, кажется, показываете в этом вопросе. Файлы '.cu' и' .cpp' могут содержать ссылки CUDA в них, которые вам придется реорганизовать. Предположительно, файлы '.cu' содержат код устройства CUDA. Они должны быть переписаны как функции C/C++. Простые ядра CUDA можно переписать с набором циклов вокруг кода ядра, но нет общей дорожной карты. –

+0

Спасибо @ RobertCrovella - Я боялся этого. Если вы отправите свой комментарий в качестве ответа, я соглашусь с ним. –

ответ

1

В общем случае, я не думаю, что есть специальная дорожная карта для приложения «de-CUDA-fy». Так же, как я не думаю, что есть определенная «механическая» дорожная карта для приложения «CUDA-fy», и я не нахожу конкретных дорожных карт для проблем программирования в целом.

Кроме того, я думаю, что предлагаемая дорожная карта имеет недостатки. Чтобы выбрать только один пример, файл .cu обычно будет иметь ссылки на CUDA, которые не будут допущены обычным компилятором C++, используемым для компиляции кода .cpp. Некоторые из этих ссылок могут быть элементами, которые зависят от API среды выполнения CUDA, таких как cudaMalloc и cudaMemcpy, и хотя это может быть сделано для прохождения через обычный компилятор C++ (это только вызовы библиотеки), было бы разумно оставить их в -place для приложения, которое удаляет символ CUDA. Кроме того, некоторые из ссылок могут быть специфическими языковыми функциями CUDA, такими как объявление кода устройства через __global__ или __device__ или запуск функции «ядро» устройства с соответствующим синтаксисом <<<...>>>. Эти не может быть сделано, чтобы пройти через обычный компилятор C++, и должны рассматриваться конкретно. Более того, простое удаление этих ключевых слов и синтаксиса CUDA вряд ли принесет полезные результаты.

Одним словом, код должен быть реорганизован; нет разумной краткой дорожной карты, которая объясняет более или менее механический процесс для этого. Я полагаю, что сложность процесса рефакторинга будет примерно такой же сложной, как и исходный процесс (если таковой был), чтобы преобразовать версию кода CUDA, отличную от CUDA, в версию CUDA. Как минимум, для понимания конструкций CUDA потребуются некоторые не-механические знания программирования CUDA.

Для очень простой Коды CUDA, возможно, будет возможно вынести несколько механический процесс для де-CUDA-fy кода.Напомним, основная последовательность обработки CUDA выглядит следующим образом:

  1. выделить пространство для данных на устройстве (возможно, с cudaMalloc) и скопировать данные на устройство (возможно, с cudaMemcpy)
  2. запуска функция, которая работает на устройство (функция __global__ или «ядро») для обработки данных и создания результатов
  3. результатов копирования обратно из устройства (возможно, опять-таки, с cudaMemcpy)

Таким образом, простой approac ч будет:

  1. устранить cudaMalloc/cudaMemcpy операции, в результате чего данные, представляющие интерес в его первоначальном виде, на хосте
  2. преобразования функций обработки CUDA (ядра) для обычного C++ функций, которые выполняют такая же операция с данными хоста

Поскольку CUDA представляет собой архитектуру параллельной обработки, один подход к преобразованию кода ядра CUDA по существу в обычный код C++ (шаг 2 выше) заключается в использовании цикла или набор петель. Но помимо этого дорожная карта имеет тенденцию к разрастанию, в зависимости от того, что действительно делает код. Кроме того, между нить связи, не трансформационных алгоритмы (например, сокращение), а также использование CUDA или других встроенных функций специфических языковых особенностей значительно усложнит шаг 2.

Например давайте очень простой вектор ADD код , Код ядра CUDA для этого было бы выделить ряд характеристик, которые позволяют легко преобразовать в или из реализации CUDA:

  1. Там нет межпоточной связи. Проблема заключается в «смущающей параллели». Работа, выполняемая каждым потоком, не зависит от всех других потоков. Это описывает только ограниченное подмножество кодов CUDA.

  2. Нет необходимости в использовании каких-либо специфических языковых функций или особенностей CUDA (кроме глобальной уникальной переменной индекса потока), поэтому код ядра распознается как почти полностью действующий код C++. Опять же, этот признак, вероятно, описывает только ограниченное подмножество кодов CUDA.

Таким образом, версия CUDA вектора добавить код может выглядеть следующим образом (резко упрощена для целей презентации):

#include <stdio.h> 
#define N 512 
// perform c = a + b vector add 
__global__ void vector_add(const float *a, const float *b, float *c){ 

    int idx = threadIdx.x; 
    c[idx]=a[idx]+b[idx]; 
} 

int main(){ 

    float a[N] = {1}; 
    float b[N] = {2}; 
    float c[N] = {0}; 
    float *d_a, *d_b, *d_c; 
    int dsize = N*sizeof(float); 
    cudaMalloc(&d_a, dsize); // step 1 of CUDA processing sequence 
    cudaMalloc(&d_b, dsize); 
    cudaMalloc(&d_c, dsize); 
    cudaMemcpy(d_a, a, dsize, cudaMemcpyHostToDevice); 
    cudaMemcpy(d_b, b, dsize, cudaMemcpyHostToDevice); 
    vector_add<<<1,N>>>(d_a, d_b, d_c); // step 2 
    cudaMemcpy(c, d_c, dsize, cudaMemcpyDeviceToHost); // step 3 
    for (int i = 0; i < N; i++) if (c[i] != a[i]+b[i]) {printf("Fail!\n"); return 1;} 
    printf("Success!\n"); 
    return 0; 
} 

Мы видим, что приведенный выше код следует последовательность обработки типична CUDA 1-2 -3, а начало каждого шага отмечено в комментариях.Таким образом, наш «де-CUDA-FY» дорожной карты, опять же:

  1. устранить cudaMalloc/cudaMemcpy операции, в результате чего данные, представляющие интерес в его первоначальном виде, на хосте
  2. преобразования функций обработки CUDA (ядра) к обычным функциям C++, которые выполняют ту же операцию на хосте данных

для шага 1, мы в буквальном смысле просто удалить cudaMalloc и cudaMemcpy линии, и мы вместо этого планируют работать непосредственно на a[], b[] и c[] переменных в главном коде. Таким образом, оставшийся шаг состоит в том, чтобы преобразовать функцию «ядро» CUDA 10 в обычную функцию C++. Опять-таки, некоторые знания об основах CUDA необходимы для понимания масштабов операции, выполняемой параллельно. Но сам код ядра (за исключением использования встроенной переменной CUDA) является полностью допустимым кодом C++, и нет связи между потоками или другими осложняющими факторами. Таким образом, обычный C++ реализация может быть просто код ядра, помещают в подходящий для цикла итерации по параллельной степени (N в данном случае), и помещают в C Функция сравнимой ++:

void vector_add(const float *a, const float *b, float *c){ 

    for (int idx=0; idx < N; idx++) 
    c[idx]=a[idx]+b[idx]; 
} 

Объединяя вышеуказанные шаги мы должны (в тривиальном примере):

  1. удалить cudaMalloc и cudaMemcpy операции
  2. заменить код ядра CUDA с аналогичным, обычным Си ++
  3. FixUp заклимание ядра в main быть обычным C++ вызов функции

Который дает нам:

#include <stdio.h> 
#define N 512 
// perform c = a + b vector add 
void vector_add(const float *a, const float *b, float *c){ 

    for (int idx = 0; idx < N; idx++) 
    c[idx]=a[idx]+b[idx]; 
} 

int main(){ 

    float a[N] = {1}; 
    float b[N] = {2}; 
    float c[N] = {0}; 
    vector_add(a, b, c); 
    for (int i = 0; i < N; i++) if (c[i] != a[i]+b[i]) {printf("Fail!\n"); return 1;} 
    printf("Success!\n"); 
    return 0; 
} 

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

Несколько других комментариев:

  1. Многие ноутбуки доступны, которые имеют CUDA-совместимых (т.е. NVIDIA) графических процессоров в них. Если у вас есть один из них (я понимаю, вы не я, но я включаю это для других, кто может это прочитать), вы можете, вероятно, запустить коды CUDA на нем.

  2. Если у вас есть настольный ПК, вполне вероятно, что за менее чем 100 долларов вы можете добавить к нему графический процессор с поддержкой CUDA.

  3. Пытается использовать технологию эмуляции. ИМО - это не путь сюда, если только вы не можете использовать его под ключ. На мой взгляд, выбивание битов и кусков из эмулятора в собственное приложение - это нетривиальное упражнение.

  4. Я считаю, что в общем случае преобразование кода CUDA в соответствующий код OpenCL также не будет тривиальным.(Мотивация здесь в том, что существует много сходства между CUDA и OpenCL, и, вероятно, код OpenCL может быть запущен на вашем ноутбуке, поскольку коды OpenCL обычно можно запускать по целому ряду целей, включая процессоры и графические процессоры). Есть достаточно различий между двумя технологиями, для которых требуется некоторое усилие, и это приносит дополнительное бремя, требующее некоторого уровня знакомости с как OpenCL, так и тягой вашего вопроса, похоже, хочет избежать этих кривых обучения.

Смежные вопросы