Что такое "объединенный" в транзакции глобальной памяти CUDA? Я не мог понять даже после прохождения моего гида CUDA. Как это сделать? В примере матрицы программирования CUDA, обращение к матрице по строке называется "объединенным" или col.. col.. называется coalesced? Что правильно и почему?
В CUDA, что такое объединение памяти и как оно достигается?
Ответ 1
Вероятно, эта информация применяется только для вычисления 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. Последовательный! Слившихся! Ура!
Лучший способ - это, возможно, написать свое ядро, а затем профилировать его, чтобы увидеть, есть ли у вас не коалесцированные глобальные нагрузки и магазины.
Ответ 2
Объединение памяти - это метод, который позволяет оптимально использовать пропускную способность глобальной памяти. То есть, когда параллельные потоки, выполняющие одну и ту же команду, получают доступ к последовательным местоположениям в глобальной памяти, достигается наиболее благоприятный шаблон доступа.
Пример на рисунке выше помогает объяснить объединенную компоновку:
На рис. (а) n векторов длины m сохраняются линейно. Элемент я вектора j обозначается v j i. Каждому потоку в ядре GPU присваивается один вектор длины m. Темы в CUDA сгруппированы в массив блоков, и каждый поток в GPU имеет уникальный идентификатор, который может быть определен как indx=bd*bx+tx
, где bd
представляет размер блока, bx
обозначает индекс блока, а tx
- это поток индекс в каждом блоке.
Вертикальные стрелки демонстрируют, что параллельные потоки получают доступ к первым компонентам каждого вектора, то есть адресуют 0, m, 2m... памяти. Как показано на рисунке (а), в этом случае доступ к памяти не является последовательным. Путем обнуления разрыва между этими адресами (красные стрелки, показанные на рисунке выше) доступ к памяти становится объединенным.
Однако проблема здесь немного сложна, так как разрешенный размер проживающих потоков на каждый блок графического процессора ограничен bd
. Поэтому объединение данных с помощью коалесценции может быть выполнено путем сохранения первых элементов первых векторов bd
в последовательном порядке, за которыми следуют первые элементы вторых векторов bd и т.д. Остальные элементы векторов хранятся аналогичным образом, как показано на рис. (B). Если n (число векторов) не является фактором bd
, необходимо вставить оставшиеся данные в последнем блоке с некоторым тривиальным значением, например. 0.
В линейном хранилище данных на фиг. (a) компонент я (0 ≤ я < m) векторного indx
(0 ≤ indx < n) адресуется на m × indx +i
; тот же компонент в объединенном
(b) рассматривается как
(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.
источник: "ускорение вычислений на основе графического процессора в нелинейном анализе деформаций конечных элементов". Международный журнал для численных методов в биомедицинской инженерии (2013).
Ответ 3
Если потоки в блоке обращаются к последовательным местоположениям глобальной памяти, тогда все обращения объединяются в один запрос (или объединены) аппаратным обеспечением. В примере матрицы матричные элементы в строке располагаются линейно, за ними следуют следующая строка и т.д. Например, например, 2x2 матрица и 2 потока в блоке, ячейки памяти расположены как:
(0,0) (0,1) (1,0) (1,1)
В доступе к строке, thread1 получает доступ (0,0) и (1,0), которые невозможно скомбинировать. При доступе к столбцам thread1 обращается (0,0) и (0,1), которые могут быть объединены, поскольку они смежны.
Ответ 4
Критерии коалесценции хорошо описаны в CUDA 3.2 Programming Guide, раздел G.3.2. Краткая версия выглядит так: потоки в warp должны последовательно обращаться к памяти, а слова, к которым осуществляется доступ, должны >= 32 бит. Кроме того, базовый адрес, к которому обращается warp, должен быть выровнен по 64, 128 или 256 байтам для 32-, 64- и 128-битных обращений соответственно.
Аппаратные средства Tesla2 и Fermi отлично справляются с совместными 8- и 16-разрядными обращениями, но их лучше избегать, если вам нужна максимальная пропускная способность.
Обратите внимание, что, несмотря на улучшения в аппаратных средствах Tesla2 и Fermi, коалесценция не является устаревшей. Даже на оборудовании класса Tesla2 или Fermi, неспособное объединить транзакции глобальной памяти, может привести к поражению в 2 раза. (На аппаратном уровне Fermi это кажется правдой только тогда, когда включен ECC. Непрерывные транзакции памяти с нечеткой памятью совершают около 20% удара по Ферми.)