Как использовать объединенный доступ к памяти - PullRequest
8 голосов
/ 03 июля 2011

У меня есть «N» потоки для одновременного выполнения на устройстве, которое им нужно. M * N плавают из глобальной памяти. Как правильно соединить глобальную память? В этом вопросе, как может помочь общая память?

1 Ответ

15 голосов
/ 03 июля 2011

Обычно хороший объединенный доступ может быть достигнут, когда соседние потоки обращаются к соседним ячейкам в памяти. Итак, если tid содержит индекс вашей нити, то доступ к:

  • arr[tid] --- дает идеальное слияние
  • arr[tid+5] --- почти идеально, возможно, выровнено
  • arr[tid*4] --- больше не так хорош из-за пробелов
  • arr[random(0..N)] --- ужасно!

Я говорю с точки зрения программиста CUDA, но аналогичные правила применяются и в других местах, даже при простом программировании ЦП, хотя влияние здесь не так велико.


"Но у меня так много массивов, что у каждого примерно в 2 или 3 раза больше, чем количество моих потоков, и использование шаблона типа" arr [tid * 4] "неизбежно. Что может быть лекарством от этого ? "

Если смещение кратно некоторой более высокой 2-степени (например, 16 * x или 32 * x), это не проблема. Итак, если вам нужно обработать довольно длинный массив в цикле for, вы можете сделать что-то вроде этого:

for (size_t base=0; i<arraySize; i+=numberOfThreads)
    process(arr[base+threadIndex])

(в приведенном выше примере размер массива равен , кратному от числа потоков)

Итак, если число потоков кратно 32, доступ к памяти будет хорошим.

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


Относится ли "32" к размеру деформации, которая параллельна глобальной памяти?

Хотя не напрямую, есть некоторая связь. Глобальная память разделена на сегменты по 32, 64 и 128 байтов, к которым обращаются полусферы. Чем больше сегментов вы используете для данной инструкции извлечения памяти, тем дольше она идет. Подробнее об этом можно прочитать в «Руководстве по программированию CUDA», в этом разделе есть целая глава: «5.3. Максимальная пропускная способность памяти».

Кроме того, я немного слышал об общей памяти для локализации доступа к памяти. Является ли это предпочтительным для объединения памяти или имеет свои трудности? Совместно используемая память намного быстрее, поскольку она находится на кристалле, но ее размер ограничен. Память не сегментирована как глобальная, доступ к ней можно получить практически случайно, без каких-либо штрафов. Однако есть строки банка памяти шириной 4 байта (размер 32-битного целого). Адрес памяти, к которому обращается каждый поток, должен быть разным по модулю 16 (или 32, в зависимости от графического процессора). Таким образом, адрес [tid*4] будет намного медленнее, чем [tid*5], поскольку первый из них имеет доступ только к банкам 0, 4, 8, 12, а последние 0, 5, 10, 15, 4, 9, 14, ... ( идентификатор банка = адрес по модулю 16).

Опять же, вы можете прочитать больше в Руководстве по программированию CUDA.

...