#cuda
#cuda
Вопрос:
Это мой код. У меня много потоков, так что эти потоки вызывают эту функцию много раз. Внутри этой функции я создаю массив. Это эффективная реализация?? Если это не так, пожалуйста, предложите мне эффективную реализацию.
__device__ float calculate minimum(float *arr)
{
float vals[9]; //for each call to this function I am creating this arr
// Is it efficient?? Or how can I implement this efficiently?
// Do I need to deallocate the memory after using this array?
for(int i=0;i<9;i )
vals[i] = //call some function and assign the values
float min = findMin(vals);
return min;
}
Комментарии:
1. @mbx: Это CUDA. По умолчанию стека нет, функции компилируются встроенно.
2. Так что в любом случае должно быть эффективным 🙂 Спасибо за ваш совет. Я лучше удалю свой первый комментарий, чтобы не смущать других читателей.
Ответ №1:
В этом коде нет «создания массива». Существует статически объявленный массив. Кроме того, стандартная модель компиляции CUDA будет встроенным расширением __device__
функций, что означает, что vals
они будут скомпилированы в локальной памяти или, если возможно, даже в регистрах.
Все это происходит во время компиляции, а не во время выполнения.
Комментарии:
1. @Green Code: Да, очевидно. Это
__device__
функция — она компилируется во встроенный код GPU в ядре CUDA, которое ее вызывает. В этой модели компиляции существует четыре типа памяти — глобальная, совместно используемая, постоянная и локальная.2. Не могли бы вы подробнее рассказать об этих запоминающих устройствах, их спецификациях и их размере (я только что задал тот же вопрос в gamedev.stackexchange)?
3. Все это очень подробно описано в руководстве по программированию на CUDA, если вы хотите его прочитать.
Ответ №2:
Возможно, я чего-то не понимаю, но из опубликованного вами кода следует, что временный массив вам вообще не нужен. Ваш код будет (немного) быстрее, если вы сделаете что-то подобное:
#include "float.h" // for FLT_MAX
__device__ float calculate minimum(float *arr)
{
float minVal = FLT_MAX:
for(int i=0;i<9;i )
thisVal = //call some function and assign the values
minVal = min(thisVal,minVal);
return minVal;
}
Там, где действительно требуется массив, нет ничего плохого в объявлении его таким образом (как говорили многие другие).
Ответ №3:
Что касается «float vals[9]», это будет эффективно в CUDA. Для массивов небольшого размера компилятор почти наверняка распределит все элементы по регистрам напрямую. Итак, «vals [0]» будет регистром, «vals[1]» будет регистром и т.д.
Если у компилятора начинают заканчиваться регистры или размер массива превышает примерно 16, то используется локальная память. Вам не нужно беспокоиться о выделении / освобождении локальной памяти, компилятор / драйвер сделают все это за вас.
Устройства с вычислительными возможностями 2.0 и выше имеют стек вызовов, позволяющий выполнять такие вещи, как рекурсия. Например, вы можете установить размер стека равным 6 КБ на поток с помощью:
cudaStatus = cudaThreadSetLimit(cudaLimitStackSize, 1024*6);
Обычно вам не нужно будет самостоятельно прикасаться к стеку. Даже если вы добавите большие статические массивы в функции вашего устройства, компилятор и драйвер увидят, что там есть, и освободят для вас место.
Комментарии:
1. Это не совсем правильно. Если массив динамически индексируется, массив ДОЛЖЕН храниться в памяти устройства (вне чипа), поскольку файл регистра GPU не является динамически адресуемым (индексы регистров должны быть непосредственными значениями в скомпилированной сборке). Итак, если
i
invals[i]
вычисляется динамически,vals
он не будет сохранен в регистрах, независимо от того, насколько он мал. В приведенном выше случае вы могли бы#pragma unroll
выполнить цикл, чтобы убедиться, что онi
не является динамическим.