CUDA - Объединение доступа к памяти и ширины шины

Таким образом, у меня есть идея объединить доступ к памяти в CUDA, что потоки в деформации должны обращаться к смежным адресам памяти, так как это вызовет только одну транзакцию памяти (значения каждого адреса затем передаются потокам) вместо нескольких те, которые будут выполнены в последовательном порядке.

Теперь ширина моего автобуса составляет 48 байтов. Это означает, что я могу передавать 48 байтов на каждую транзакцию памяти, верно? Итак, чтобы в полной мере использовать шину, мне нужно было бы иметь возможность читать 48 байтов за раз (считывая более одного байта на поток - транзакции памяти выполняются деформацией). Однако, гипотетически, разве одно чтение не может обеспечить одновременное чтение 48 байтов за один раз (я предполагаю, что я могу читать 48 байтов за раз, читая структуру, размер которой составляет 48 байтов)?

Моя проблема с объединением - это транспонирование, которое я должен сделать с данными. У меня много данных, поэтому для их переноса требуется время, которое я бы предпочел использовать для чего-то другого, если бы мог.

Я на Compute Capability 2.0.

2 ответа

Решение

Шина памяти вашего графического процессора не просто 48 байтов в ширину (что было бы довольно громоздко, поскольку это не степень 2). Вместо этого он состоит из 6 каналов памяти по 8 байтов (64 бита) каждый. Операции с памятью обычно намного шире, чем ширина канала, чтобы использовать режим пакетной памяти. Хорошие размеры транзакций начинаются с 64 байт, чтобы создать пакет размером 8, который хорошо сочетается с 16 32-битными словами полусфраз на устройствах с вычислительной способностью 1.x.

Транзакции шириной 128 байт все еще немного быстрее и соответствуют 32-битному доступу к деформируемому файлу для устройств с вычислительной способностью 2.0 (и выше). Строки кэша также имеют ширину 128 байт для соответствия. Обратите внимание, что все эти доступы должны быть выровнены по кратной ширине транзакции, чтобы отобразить одну транзакцию памяти.

Что касается вашей реальной проблемы, то, вероятно,лучше всего ничего не делать и позволить кешу разобраться. Это работает так же, как вы бы явно делали в разделяемой памяти, просто это делается для вас аппаратным обеспечением кеша, и для этого не требуется никакого кода, что должно сделать его немного быстрее. Единственное, о чем нужно беспокоиться, это иметь достаточное количество доступного кеша, чтобы каждая деформация могла иметь необходимые 32×32×4 байта = 4 Кбайт кеша для ширины слова (например, с плавающей запятой) или 8 Кбайт для двойного доступа. Это означает, что может быть полезно ограничить количество деформаций, которые активны в одно и то же время, чтобы они не перебивали строки кэша друг друга.

Для специальных оптимизаций есть также возможность использовать векторные типы, такие какfloat2или жеfloat4поскольку все графические процессоры с поддержкой CUDA имеют инструкции загрузки и хранения, которые отображают 8 или 16 байтов в один и тот же поток. Однако на вычислительных возможностях 2.0 и выше я не вижу особого преимущества использования их в случае транспонирования общей матрицы, поскольку они еще больше увеличивают объем кеша каждой деформации.

Поскольку настройка по умолчанию для кэша 16 КБ / 48 КБ совместно используемой памяти позволяет использовать только четыре деформации для каждого SM для одновременного выполнения транспонирования (при условии, что у вас нет других обращений к памяти одновременно), вероятно, целесообразно выбрать кэш / 48 КБ / Настройка общей памяти 16 КБ по сравнению с разделением по умолчанию 16 КБ / 48 КБ с использованиемcudaDeviceSetCacheConfig(),

Для полноты я также упомяну, что инструкции перестановки деформации, представленные с возможностью вычисления 3.0, позволяют обмениваться данными регистров внутри деформации, не проходя через кеш или разделяемую память. См. Приложение B.14 Руководства по программированию CUDA C для получения более подробной информации.
(Обратите внимание, что версия Руководства по программированию существует без этого приложения. Поэтому, если в вашем экземпляре Приложение B.13 речь идет о чем-то другом, перезагрузите его по предоставленной ссылке).

В целях объединения, как вы заявили, вы должны сосредоточиться на том, чтобы 32 потока находились в смежных местах деформации, предпочтительно также выровненных по 32 или 128 байтов. Кроме того, не беспокойтесь о шине физического адреса памяти DRAM. Контроллер памяти состоит в основном из независимых разделов, каждый из которых имеет ширину 64 бита. Ваш объединенный доступ, выходящий из основы, будет максимально быстро удовлетворен контроллером памяти. Единый объединенный доступ для полного деформирования (32 потока), обращающегося к int или float, потребует в любом случае получения 128 байтов, т.е. нескольких транзакций на физической шине для DRAM. Когда вы работаете в режиме кэширования, вы все равно не можете контролировать степень детализации запросов к глобальной памяти ниже 128 байт за раз.

Невозможно заставить один поток запрашивать 48 байтов или что-то подобное в одной транзакции. Даже на уровне кода c, если вы думаете, что получаете доступ ко всей структуре данных одновременно, на уровне машинного кода он преобразуется в инструкции, которые читают 32 или 64 бита за раз.

Если вы чувствуете, что ограничение кэширования в 128 байтов за раз наказывает ваш код, вы можете попробовать запустить его в режиме без кэширования, что снизит степень детализации запросов глобальной памяти до 32 байтов за раз. Если у вас разбросанный шаблон доступа (не очень хорошо слитый), эта опция может дать лучшую производительность.

Другие вопросы по тегам