Это будет немного умозрительно, но может добавить к ответу @ArchaeaSoftware.
В основном я знаком с Compute Capability 2.0 (Fermi). Я не думаю, что для этой архитектуры есть какой-либо выигрыш в производительности от использования векторизованных типов, за исключением, может быть, 8- и 16-битных типов.
Глядя на объявление для char4:
struct __device_builtin__ __align__(4) char4
{
signed char x, y, z, w;
};
Тип выровнен по 4 байтам. Я не знаю, что делает __device_builtin__
. Может быть, это вызывает какую-то магию в компиляторе...
Все выглядит немного странно для объявлений float1
, float2
, float3
и float4
:
struct __device_builtin__ float1
{
float x;
};
__cuda_builtin_vector_align8(float2, float x; float y;);
struct __device_builtin__ float3
{
float x, y, z;
};
struct __device_builtin__ __builtin_align__(16) float4
{
float x, y, z, w;
};
float2
получает особое обращение. float3
— это структура без какого-либо выравнивания, а float4
выравнивается по 16 байтам. Я не уверен, что с этим делать.
Транзакции глобальной памяти составляют 128 байт, выровненные по 128 байтам. Транзакции всегда выполняются для полного варпа за раз. Когда варп достигает функции, которая выполняет транзакцию памяти, скажем, 32-битную загрузку из глобальной памяти, чип в это время выполняет столько транзакций, сколько необходимо для обслуживания всех 32 потоков в варпе. Таким образом, если все доступные 32-битные значения находятся в пределах одной 128-байтовой строки, необходима только одна транзакция. Если значения поступают из разных 128-байтовых строк, выполняется несколько 128-байтовых транзакций. Для каждой транзакции деформация приостанавливается примерно на 600 циклов, пока данные извлекаются из памяти (если только они не находятся в кэшах L1 или L2).
Итак, я думаю, что ключом к выяснению того, какой тип подхода дает наилучшую производительность, является рассмотрение того, какой подход вызывает наименьшее количество 128-байтных транзакций памяти.
Предполагая, что встроенные векторные типы являются просто структурами, некоторые из которых имеют специальное выравнивание, использование векторных типов приводит к тому, что значения сохраняются в памяти чередующимся образом (массив структур). Таким образом, если варп загружает все значения x
в этой точке, другие значения (y
, z
, w
) будут загружены в L1 из-за 128-байтовых транзакций. Когда позже варп попытается получить к ним доступ, возможно, они больше не находятся в L1, и поэтому должны быть запущены новые транзакции глобальной памяти. Кроме того, если компилятор может выдавать более широкие инструкции для чтения большего количества значений одновременно, для будущего использования он будет использовать регистры для хранения тех, которые находятся между точкой загрузки и точкой использования, возможно, увеличивая использование регистров. ядра.
С другой стороны, если значения упакованы в структуру массивов, нагрузку можно обслуживать с минимальным количеством транзакций. Итак, при чтении из массива x
в 128-байтных транзакциях загружается только x
значений. Это может привести к меньшему количеству транзакций, меньшей зависимости от кешей и более равномерному распределению между вычислительными операциями и операциями с памятью.
person
Roger Dahl
schedule
09.09.2012