Подтвердить что ты не робот

Использование cudamalloc(). Почему двойной указатель?

В настоящее время я изучаю примеры учебников на http://code.google.com/p/stanford-cs193g-sp2010/, чтобы узнать CUDA. Код, который демонстрирует функции __global__, приведен ниже. Он просто создает два массива: один на процессоре и один на графическом процессоре, заполняет массив GPU номером 7 и копирует данные массива GPU в массив CPU.

#include <stdlib.h>
#include <stdio.h>

__global__ void kernel(int *array)
{
  int index = blockIdx.x * blockDim.x + threadIdx.x;

  array[index] = 7;
}

int main(void)
{
  int num_elements = 256;

  int num_bytes = num_elements * sizeof(int);

  // pointers to host & device arrays
  int *device_array = 0;
  int *host_array = 0;

  // malloc a host array
  host_array = (int*)malloc(num_bytes);

  // cudaMalloc a device array
  cudaMalloc((void**)&device_array, num_bytes);

  int block_size = 128;
  int grid_size = num_elements / block_size;

  kernel<<<grid_size,block_size>>>(device_array);

  // download and inspect the result on the host:
  cudaMemcpy(host_array, device_array, num_bytes, cudaMemcpyDeviceToHost);

  // print out the result element by element
  for(int i=0; i < num_elements; ++i)
  {
    printf("%d ", host_array[i]);
  }

  // deallocate memory
  free(host_array);
  cudaFree(device_array);
} 

Мой вопрос, почему они сформулировали оператор cudaMalloc((void**)&device_array, num_bytes); с помощью двойного указателя? Даже здесь определение cudamalloc() on говорит, что первым аргументом является двойной указатель.

Почему бы просто не вернуть указатель на начало выделенной памяти на GPU, как функция malloc на CPU?

4b9b3361

Ответ 1

Все функции API CUDA возвращают код ошибки (или cudaSuccess, если ошибка не возникла). Все остальные параметры передаются по ссылке. Однако на простом C вы не можете иметь ссылки, поэтому вам нужно передать адрес переменной, по которой вы хотите сохранить возвращаемую информацию. Поскольку вы возвращаете указатель, вам нужно передать двойной указатель.

Другая известная функция, которая работает по адресам по той же причине, - это функция scanf. Сколько раз вы забыли написать этот & перед переменной, в которую вы хотите сохранить значение?;)

int i;
scanf("%d",&i);

Ответ 2

Это просто ужасный, ужасный дизайн API. Проблема с передачей двойных указателей для функции распределения, которая получает абстрактную (void *) память, заключается в том, что вы должны сделать временную переменную типа void * для хранения результата, а затем назначить ее в реальный указатель правильного типа вы хотите использовать. Кастинг, как и в (void**)&device_array, недействителен C и приводит к поведению undefined. Вы должны просто написать функцию-оболочку, которая ведет себя как обычный malloc и возвращает указатель, как в:

void *fixed_cudaMalloc(size_t len)
{
    void *p;
    if (cudaMalloc(&p, len) == success_code) return p;
    return 0;
}

Ответ 3

Мы переводим его в двойной указатель, потому что это указатель на указатель. Он должен указывать на указатель памяти GPU. Что делает cudaMalloc(), так это то, что он выделяет указатель памяти (с пробелом) на GPU, который затем указывается первым аргументом, который мы даем.

Ответ 4

Проблема: вам нужно вернуть два значения: Обратный код И указатель на память (в случае, если код возврата указывает на успех). Поэтому вы должны сделать один из них указателем на возвращаемый тип. И в качестве типа возврата у вас есть выбор между обратным указателем на int (для кода ошибки) или указателем возврата на указатель (для адреса памяти). Там одно решение так же хорошо, как и другое (и один из них дает указатель на указатель (я предпочитаю использовать этот термин вместо двойного указателя, поскольку это больше похоже на указатель на число с двойной плавающей запятой)).

В malloc у вас есть приятное свойство, что у вас могут быть указатели на указатели, указывающие на ошибку, поэтому вам в основном нужно только одно возвращаемое значение. Я не уверен, возможно ли это с указателем на память устройства, поскольку это может быть что нет или неправильное значение null (помните: это CUDA и НЕ Ansi C). Может быть, нулевой указатель на хост-системе полностью отличается от нулевого значения, используемого для устройства, и поэтому возвращение нулевого указателя для указания ошибок не работает, и вы должны сделать API таким образом (это также означало бы что у вас нет общего NULL на обоих устройствах).

Ответ 5

В C/С++ вы можете выделить блок памяти динамически во время выполнения, вызвав функцию malloc.

int * h_array
h_array = malloc(sizeof(int))

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

  • cudamalloc возвращает целое число как код ошибки вместо указатель на блок памяти.
  • В дополнение к размеру байта, который должен быть выделенное, cudamalloc также требует указатель двойной пустоты, как его первый параметр.

    int * d_array cudamalloc ((void **) & d_array, sizeof (int))

Причина первого различия заключается в том, что вся функция API CUDA следует за соглашением о возврате целочисленного кода ошибки. Поэтому, чтобы согласовать все, API cudamalloc также возвращает целое число.

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

Во-первых, поскольку мы уже решили сделать cudamalloc возвратом целочисленного значения, мы больше не можем использовать его для возврата адреса выделенной памяти. В C единственным другим способом для связи функции является передача указателя или адреса функции. Функция может вносить изменения в значение, хранящееся по адресу или адресу, на который указывает указатель. Изменения этого значения могут быть позже извлечены за пределы области действия, используя тот же адрес памяти.

как работает двойной указатель

На следующей диаграмме показано, как она работает с двойным указателем.

int cudamalloc((void **) &d_array, int type_size) {
  *d_array = malloc(type_size)
  return return_code
}

введите описание изображения здесь

Зачем нам нужен двойной указатель? Почему это работает

Я обычно живу в мире python, поэтому я также пытался понять, почему это не сработает.

int cudamalloc((void *) d_array, int type_size) {
  d_array = malloc(type_size)
  ...
  return error_status
}

Так почему это не работает? Поскольку в C, когда вызывается cudamalloc, создается локальная переменная с именем d_array и назначается значением первого аргумента функции. Мы не можем получить значение в этой локальной переменной вне области действия. Вот почему нам нужен указатель на указатель здесь.

int cudamalloc((void *) d_array, int type_size) {
  *d_array = malloc(type_size)
  ...
  return return_code
}

введите описание изображения здесь