В CUDA, что такое слияние памяти, и как это достигается?


Что "сливается" в глобальной транзакции памяти CUDA? Я не мог понять даже после прохождения моего руководства CUDA. Как это сделать? В Примере матрицы руководства по программированию CUDA доступ к матрице строка за строкой называется "coalesced" или col.. на седловине.. это называется коалесцировать? Что правильно и почему?

4 58

4 ответа:

вероятно, что эта информация применяется только для вычисления capabality 1.x, или cuda 2.0. Более поздние архитектуры и cuda 3.0 имеют более сложный глобальный доступ к памяти, и фактически "объединенные глобальные нагрузки" даже не профилируются для этих чипов.

кроме того, эта логика может быть применена к общей памяти, чтобы избежать конфликтов.


объединенное транзакционной памяти является та, в которой все потоки в варп открыть глобальная память в то же время. Это слишком просто, но правильный способ сделать это-просто иметь последовательные потоки доступа к последовательным адресам памяти.

Итак, если потоки 0, 1, 2 и 3 читают глобальную память 0x0, 0x4, 0x8 и 0xc, это должно быть объединенное чтение.

в Примере матрицы, имейте в виду, что вы хотите, чтобы ваша матрица проживать линейно в памяти. Вы можете сделать это, как вы хотите, и ваш доступ к памяти должен отражать, как ваша матрица выложена. Итак, матрица 3x4 ниже

0 1 2 3
4 5 6 7
8 9 a b

может быть сделано строка за строкой, как это, так что (r,c) карты в память (r*4 + c)

0 1 2 3 4 5 6 7 8 9 a b

Предположим, вам нужно получить доступ к элементу один раз, и сказать, что у вас есть четыре потока. Какие потоки будут использоваться для какого элемента? Вероятно, либо

thread 0:  0, 1, 2
thread 1:  3, 4, 5
thread 2:  6, 7, 8
thread 3:  9, a, b

или

thread 0:  0, 4, 8
thread 1:  1, 5, 9
thread 2:  2, 6, a
thread 3:  3, 7, b

что лучше? Что приведет к Объединенным чтениям, а что нет?

в любом случае, каждый поток делает три доступов. Давайте посмотрим на первый доступ и посмотреть, если потоки обращаются к памяти последовательно. В первом варианте, первый выход равен 0, 3, 6, 9. Не последовательные, не сросшиеся. Второй вариант, это 0, 1, 2, 3. Подряд! Соединились! Ура!

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

объединение памяти-это метод, который позволяет оптимально использовать глобальную пропускную способность памяти. То есть, когда параллельные потоки выполняют одну и ту же инструкцию доступа к последовательным местоположениям в глобальной памяти, достигается наиболее благоприятный шаблон доступа.

enter image description here

пример на рисунке выше помогает объяснить срослись расположение:

На Фиг. (а), n векторов длины m хранятся в линейном мода. Элемент я вектор j обозначается vjя. Каждый поток в ядре GPU присваивается одному m-длина вектора. Потоки в CUDA сгруппированы в массив блоков, и каждый поток в GPU имеет уникальный идентификатор, который можно определить как indx=bd*bx+tx, где bd представляет собой блок измерения, bx обозначает индекс блока и tx - это индекс потока в каждом блоке.

вертикальная стрелки показывают случай, когда параллельные потоки обращаются к первым компонентам каждого вектора, т. е. адреса 0,m,... памяти. Как показано на фиг. (а), в этом случае доступ к памяти не подряд. При обнулении промежутка между этими адресами (красные стрелки показаны на рисунке выше) доступ к памяти становится Объединенным.

однако, проблема становится немного сложнее здесь, так как допустимый размер проживающих потоков на блок GPU является ограничено bd. Поэтому объединение данных может быть сделано путем хранения первых элементов первого bd векторы в последовательном порядке, за которыми следуют первые элементы вторых векторов bd и так далее. Остальные элементы векторов хранятся аналогичным образом, как показано на фиг. (си.) Если n (количество векторов) не является фактором bd, необходимо дополнить оставшиеся данные в последнем блоке некоторым тривиальным значением, например 0.

в линейных данных хранение на фиг. (a), компонент я (0 ≤ я m) вектора indx (0 ≤ indx n) решается m × indx +i; один и тот же компонент в укрупненными схема хранения на фиг. (б) рассматривается как

(m × bd) ixC + bd × ixB + ixA,

здесь ixC = floor[(m.indx + j )/(m.bd)]= bx,ixB = j и ixA = mod(indx,bd) = tx.

в общем, в Примере хранения ряда векторов с размером m линейные индексация сопоставляется с укрупненными индексации в соответствии с:

m.indx +i −→ m.bd.bx +i .bd +tx

эта перестановка данных может привести к значительному увеличению пропускной способности глобальной памяти GPU.


источник: "ускорение вычислений на основе GPU в нелинейном анализе деформации конечных элементов."Международный журнал для численных методов в биомедицинской инженерии (2013).

Если потоки в блоке обращаются к последовательным глобальным ячейкам памяти, то все обращения объединяются в один запрос(или объединяются) аппаратным обеспечением. В Примере с матрицей элементы матрицы в строке расположены линейно, за ними следует следующая строка и так далее. Например, для матрицы 2x2 и 2 потоков в блоке ячейки памяти расположены следующим образом:

(0,0) (0,1) (1,0) (1,1)

в доступе к строке thread1 обращается к (0,0) и (1,0), которые не могут быть объединены. В доступ к колонке, доступы thread1 (0,0) и (0,1), которые могут быть объединены, потому что они смежны.

критерии коалесценции хорошо документированы в CUDA 3.2 руководство по программированию, Раздел G. 3.2. Короткая версия выглядит следующим образом: потоки в warp должны последовательно обращаться к памяти, а слова, к которым обращаются, должны >=32 бита. Кроме того, базовый адрес, к которому обращается warp, должен быть выровнен по 64, 128 или 256 байтам для 32 -, 64-и 128-разрядных обращений соответственно.

оборудование Tesla2 и Fermi делает хорошую работу коалесцировать 8-и 16-битные доступы, но их лучше избегать, если вы хотите пиковую пропускную способность.

обратите внимание, что, несмотря на улучшения в аппаратных средствах Tesla2 и Fermi, Коалесценция никоим образом не устарела. Даже на аппаратном обеспечении класса Tesla2 или Fermi неспособность объединить глобальные транзакции памяти может привести к снижению производительности в 2 раза. (На аппаратном обеспечении класса Fermi это, кажется, верно только тогда, когда ECC включен. Непрерывные, но несвязанные транзакции памяти занимают около 20% от Ферми.)