Кривые когерентности, локальности и заполнения пространства CUDA / OpenCL
Я работаю над приложением CUDA, которое использует всю доступную оперативную память на карте, и пытаюсь найти разные способы уменьшить потери кеша.
Проблемная область состоит из большой 2- или 3-мерной сетки, в зависимости от типа решаемой проблемы. (Для тех, кто заинтересован, это симулятор FDTD). Каждый элемент зависит от двух или четырех элементов в "параллельных" массивах (то есть другого массива с почти одинаковыми размерами), поэтому ядра должны иметь доступ к трем или шести различным массивам.
Эта проблема
* Надеюсь, это не "слишком локализовано". Не стесняйтесь редактировать вопрос
Отношения между тремя массивами могут быть визуализированы как (извиняюсь за посредственное искусство ASCII)
A[0,0] -C[0,0]- A ---- C ---- A ---- C ---- A
| | | |
| | | |
B[0,0] B B B
| | | |
| | | |
A ---- C ---- A ---- C ---- A ---- C ---- A
| | | |
| | | |
B B B B
| | | |
| | | |
A ---- C ---- A ---- C ---- A ---- C ---- A
| | | |
| | | |
B B B B[3,2]
| | | |
| | | |
A ---- C ---- A ---- C ---- A ---- C ---- A[3,3]
[2,3]
Элементы, соединенные линиями, связаны между собой. Как видно выше, A[]
зависит от обоих B[]
а также C[]
, в то время как B[]
зависит только от A[]
, так же как и C[]
, Все A[]
обновляется в первом ядре, и все B[]
а также C[]
обновляются во втором проходе.
Если я объявлю эти массивы простыми 2D-массивами, я получу расширенный доступ к памяти. Для очень большого размера домена (3x3 +- 1 в сетке выше) это приводит к дефициту занятости и производительности.
Итак, я подумал о перестановке массива в виде кривой Z-порядка:
Кроме того, было бы довольно тривиально чередовать их в один массив, что должно улучшить производительность выборки, поскольку (в зависимости от порядка чередования), по крайней мере, половина элементов, требуемых для данного обновления ячейки, будет близка друг к другу. Однако мне не ясно, использует ли GPU несколько указателей данных при доступе к нескольким массивам. Если это так, то это воображаемое преимущество может стать помехой.
Вопросы
Я читал, что NVidia делает это автоматически за кулисами, используя текстурную память, или cudaArray
, Если это не так, следует ли ожидать увеличения задержки при пересечении больших промежутков (когда кривая Z переходит сверху вниз влево на высоком уровне подразделения), чтобы исключить преимущество местоположения в меньших сетках?
Разделение сетки на более мелкие блоки, которые могут поместиться в разделяемой памяти, безусловно, должно помочь, и порядок Z делает это довольно тривиальным. Должен ли я иметь отдельный проход ядра, который обновляет границы между блоками? Будут ли значительными затраты на запуск другого ядра по сравнению с ожидаемой экономией?
Есть ли реальная выгода от использования 2D против 1D массива? Я ожидаю, что память будет линейной, но не уверен, есть ли какое-то реальное значение для метафоры макета 2D-памяти, которая часто используется в литературе CUDA.
Ух ты - длинный вопрос. Спасибо за чтение и ответы на все / все это.
0 ответов
Просто чтобы убрать это из списка неотвеченных:
После большого количества тестов и экспериментов с различными компоновками, самый быстрый подход, который я нашел, состоял в том, чтобы чередовать массивы в z-порядке, чтобы большинство значений, требуемых потоком, располагались в ОЗУ рядом друг с другом. Это улучшило поведение кеша (и, следовательно, производительность). Очевидно, есть много случаев, когда Z-порядок не может удерживать требуемые значения близко друг к другу. Интересно, уменьшает ли вращение квадрантов "расстояние" между концом Z и следующим квадрантом, но я этого не пробовал.
Всем спасибо за советы.