2015-05-05 4 views
1

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

#include <stdio.h> 
    #include <stdlib.h> /* srand, rand */ 
    #include <time.h> /* time */ 

    #define num 100000 

    int *arr,max = -1; 

    int getRand() { 
     double r1=rand()/(double)RAND_MAX; // Generates value between 0 & 1 
     return (r1 * num) + 1; 
    } 
    void generateRandom(int M) { 
     int i; 
     for(i=0;i<M;i++) { 
      arr[i] = getRand(); 
     } 
    } 
    void getMax(int M) { 
     int i; 
     for(i=0;i<M;i++) { 
      if(arr[i] > max) 
       max = arr[i]; 
      } 
    } 

    int main(int argc, char *argv[]){ 
     if (argc == 2) { 
      int M; 
      /* initialize random seed: */ 
      srand (time(NULL)); 
      M = atoi(argv[1]); 
      //int arr[M]; 
      arr = (int*)calloc(M,sizeof(int));; 

      //printf("M = %d MAX = %d\n", M, RAND_MAX); 

      generateRandom(M); 

      getMax(M); 

      printf("Max value: %d",max); 

     } 

     else 
      printf("Invalid arguments."); 

     return 0; 
    } 

Теперь я пытаюсь преобразовать этот код в простую программу CUDA. Я попытался просто заставить функцию generateRandom работать как ядро, но у меня возникают проблемы с управлением памятью.

#include <stdio.h> 
#include <stdlib.h> /* srand, rand */ 
#include <time.h> /* time */ 
#include <cuda.h> 

#define num 100000 

int *arr,max = -1; 

int getRand() { 
    double r1=rand()/(double)RAND_MAX; // Generates value between 0 & 1 
    return (r1 * num) + 1; 
} 
void generateRandom(int M) { 
    int i; 
    for(i=0;i<M;i++) { 
     arr[i] = getRand(); 
    } 
} 
__global__ void getMax(int M) { 
    int i; 
    for(i=0;i<M;i++) { 
     if(arr[i] > max) 
      max = arr[i]; 
     } 
} 

int main(int argc, char *argv[]){ 
    if (argc == 2) { 
     int M; 
     /* initialize random seed: */ 
     srand (time(NULL)); 
     M = atoi(argv[1]); 
     //int arr[M]; 
     arr = (int*)calloc(M,sizeof(int)); 

     //printf("M = %d MAX = %d\n", M, RAND_MAX); 

     generateRandom(M); 

     getMax<<<1,1>>>(M); 

     printf("Max value: %d",max); 

    } 

    else 
     printf("Invalid arguments."); 

    return 0; 
} 

В результате этого кода произошли следующие ошибки.

cudabasic.cu(23): warning: a host variable "arr" cannot be directly read in >a device function

cudabasic.cu(23): warning: a host variable "max" cannot be directly read in >a device function

cudabasic.cu(24): warning: a host variable "arr" cannot be directly read in >a device function

cudabasic.cu(24): warning: a host variable "max" cannot be directly written >in a device function

Я гугл ошибки и выяснил, что проблема заключалась в том, что я проходил глобальные переменные ядра и, таким образом, устройство не было в состоянии прочитать его. После онлайн-предложения я попытался решить это, используя указатели вместо передачи фактических переменных, но я все еще получаю ошибки.

#include <stdio.h> 
#include <stdlib.h> /* srand, rand */ 
#include <time.h> /* time */ 
#include <cuda.h> 

#define num 100000 

int *arr,max = -1; 

int getRand() { 
    double r1=rand()/(double)RAND_MAX; // Generates value between 0 & 1 
    return (r1 * num) + 1; 
} 
void generateRandom(int M) { 
    int i; 
    for(i=0;i<M;i++) { 
     arr[i] = getRand(); 
    } 
} 
__global__ void getMax(int M, int *dArr, int *dMax) { 
    int i = threadIdx.x; 
    int a = dArr[i]; 
    for(i=0;i<M;i++) { 
     if(a > dMax) 
      dMax = a; 
     } 
} 

int main(int argc, char *argv[]){ 
    if (argc == 2) { 
     int M; 
     /* initialize random seed: */ 
     srand (time(NULL)); 
     M = atoi(argv[1]); 
     //int arr[M]; 
     arr = (int*)calloc(M,sizeof(int)); 
     devArr = (int*)cudaMalloc(M,sizeof(int)); 

     //printf("M = %d MAX = %d\n", M, RAND_MAX); 

     generateRandom(M); 

     getMax<<<1,1>>>(M, arr, max); 

     printf("Max value: %d",max); 

    } 

    else 
     printf("Invalid arguments."); 

    return 0; 
} 

cudabasic.cu(24): error: operand types are incompatible ("int" and "int *")

cudabasic.cu(25): error: a value of type "int" cannot be assigned to an >entity of type "int *"

Может кто-то мне точку в правильном направлении, как лучше идти об этом это?

Я новичок в CUDA и стараюсь понять суть, поэтому прошу прощения, если что-то, что я прошу или говорю, звучит слишком просто.

ответ

4

Лучший совет, который я могу предложить, - изучить некоторые вводные материалы программирования CUDA, такие как this. Ваш код показывает отсутствие понимания не только CUDA, но и базовых понятий C (как переменные должны быть определены до того, как они будут использоваться в выражениях.) Как программист CUDA, не «очищайте» свои знания о том, как правильно писать C или C++. Если вы используете такие вещи, как «gtc cuda intro» или «gtc cuda optimization», вы найдете хороший учебный материал CUDA.

Подход, за которым вы следуете, который должен взять однопоточный код C/C++ и преобразовать его в один поток CUDA, может дать вам несколько теплых и нечетких чувств относительно «обучения CUDA», я действительно не решаю ни одну из важных концепций - и это показывает в коде, с которым вы сейчас боретесь.

Чтобы получить последний код, представленный функционал требуется несколько шагов:

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

    getMax<<<1,1>>>(M, arr, max); 
            ^^^ ^^^ 
    

    вы были на пути к фиксации arr проблемы с вашим devArray (хотя ваш cudaMalloc не настроен должным образом), нам просто нужно исправить и завершить это с помощью дополнительной операции cudaMemcpy для копирования данных хоста на устройство. Если вы не знаете, как использовать такую ​​функцию, как cudaMalloc, не просто угадывайте свой путь через нее и применяйте отливки для использования типов для других типов - это часто является признаком того, что вы не правильно ее обрабатываете:

    devArr = (int*)cudaMalloc(M,sizeof(int)); 
    

    вместо этого обратитесь к documentation. Нам также нужно правильно обрабатывать max - это в настоящее время указатель на хост, и нам понадобится копия данных этого устройства.

  2. Ваше ядро ​​также немного смешано. Поскольку вы запускаете только одну нить CUDA, ваша threadIdx.x переменная будет только (когда) будет равна нулю:

    int i = threadIdx.x; 
    int a = dArr[i]; 
    

    Но для петли в ядре будет работать, нам нужно просто переместить некоторые линии вокруг.

  3. Несмотря на то, что вы не дошли до точки компилируемого, исполняемого кода, всегда полезно делать proper cuda error checking. Я добавил свою версию к приведенному ниже коду.

Следующий код имеет вышеуказанные проблемы решены, и, кажется, возвращает вменяемый результат:

#include <stdio.h> 
#include <stdlib.h> /* srand, rand */ 
#include <time.h> /* time */ 
#include <cuda.h> 

#define num 100000 

#define cudaCheckErrors(msg) \ 
    do { \ 
     cudaError_t __err = cudaGetLastError(); \ 
     if (__err != cudaSuccess) { \ 
      fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \ 
       msg, cudaGetErrorString(__err), \ 
       __FILE__, __LINE__); \ 
      fprintf(stderr, "*** FAILED - ABORTING\n"); \ 
      exit(1); \ 
     } \ 
    } while (0) 


int *arr,my_max = -1; 

int getRand() { 
    double r1=rand()/(double)RAND_MAX; // Generates value between 0 & 1 
    return (r1 * num) + 1; 
} 
void generateRandom(int M) { 
    int i; 
    for(i=0;i<M;i++) { 
     arr[i] = getRand(); 
    } 
} 
__global__ void getMax(int M, int *dArr, int *dMax) { 
    for(int i=0;i<M;i++) { 
     int a = dArr[i]; 
     if(a > *dMax) 
      *dMax = a; 
     } 
} 

int main(int argc, char *argv[]){ 
    if (argc == 2) { 
     int M; 
     int *devArr, *devMax; 
     /* initialize random seed: */ 
     srand (time(NULL)); 
     M = atoi(argv[1]); 
     //int arr[M]; 
     arr = (int*)calloc(M,sizeof(int)); 
     cudaMalloc(&devArr,M*sizeof(int)); 
     cudaCheckErrors("cudaMalloc 1 fail"); 
     cudaMalloc(&devMax,sizeof(int)); 
     cudaCheckErrors("cudaMalloc 2 fail"); 
     cudaMemset(devMax, 0, sizeof(int)); 
     cudaCheckErrors("cudaMemset fail"); 
     //printf("M = %d MAX = %d\n", M, RAND_MAX); 

     generateRandom(M); 
     cudaMemcpy(devArr, arr, M*sizeof(int), cudaMemcpyHostToDevice); 
     cudaCheckErrors("cudaMemcpy 1 fail"); 
     getMax<<<1,1>>>(M, devArr, devMax); 
     cudaMemcpy(&my_max, devMax, sizeof(int), cudaMemcpyDeviceToHost); 
     cudaCheckErrors("cudaMemcpy 2/kernel fail"); 
     printf("Max value: %d \n", my_max); 

    } 

    else 
     printf("Invalid arguments."); 

    return 0; 
} 

После того, как вы поняли вышеуказанные изменения, вы хотите, чтобы вернуться к моему оригиналу советы и получить организованное обучение CUDA. В этот момент, если вы хотите повторно посетить макс-поиск, тогда «хороший» способ сделать это с помощью правильной техники параллельной редукции. «Редукция» - это алгоритм, который принимает (большой) набор данных и возвращает в результате одно число или небольшой набор чисел. Поиск max в массиве является примером «сокращения». Вы можете узнать больше о правильных параллельных сокращениях CUDA, изучив this и работая через параллельное сокращение CUDA sample code.

+0

Привет, спасибо за ваши объяснения, они были очень полезны. Причина, по которой я использовал threadIdx.x, состояла в том, что я хочу, чтобы этот код использовал несколько потоков (я думаю, я немного опережал себя). Могли бы вы показать мне, как это можно сделать? Также я попытался посмотреть пример кода, с которым вы связались, но я могу выяснить, где искать код. Сокращение списков страниц - CUDA Parallel Reduction и поддерживаемые версии, но я не понимаю, как просматривать фактический код. – Lesha

+0

Я обновил свое оригинальное сообщение, чтобы включить мою первую попытку, к сожалению, она не очень удалась. Я собираюсь посмотреть, смогу ли я это исправить, но если бы вы могли предложить некоторые советы, которые были бы очень полезными. – Lesha

+0

Выполнение оптовых изменений на ваш вопрос делает мой ответ запутанным для будущих читателей. Я предлагаю задать новый вопрос. SO не предназначен для сеанса чата или для запуска диалогового окна. У вашего «нового» кода все еще есть серьезные недостатки. Например, вы запускаете 64 блока по 1 поток каждый. В этом случае 'threadIdx.x' будет * still * всегда равным нулю. Попытка собрать знания CUDA таким образом очень утомительна. Почему бы не использовать некоторые из материалов, которые я связал? Если бы вы это сделали, вы бы поняли, почему 'threadIdx.x' будет * still * всегда равным нулю в вашем новом коде. –

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