Расчет адреса загрузки при использовании инструкций по сбору AVX2

Глядя на встроенную документацию AVX2, вы найдете инструкции по загрузке, такие как VPGATHERDD:

__m128i _mm_i32gather_epi32 (int const * base, __m128i index, const int scale);

Из документации мне неясно, является ли вычисленный адрес загрузки адресом элемента или байтовым адресом, то есть адресом загрузки элемента i:

load_addr = base + index[i] * scale;               // (1) element addressing ?

или же:

load_addr = (char *)base + index[i] * scale;       // (2) byte addressing ?

Из документации Intel это выглядит так (2), но это не имеет особого смысла, учитывая, что наименьший размер элемента для собранных нагрузок составляет 32 бита - зачем вам загружать с неправильно выровненных адресов (то есть использовать масштаб < 4)

3 ответа

Решение

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

Другая причина - последовательность. С адресацией SIB у нас, очевидно, есть байтовый адрес:

MOV eax, [rcx + rdx * 2]

поскольку VPGATHERDD это просто векторизованный вариант этого MOV инструкция, мы не должны ожидать ничего другого с адресацией VSIB:

VPGATHERDD ymm0, [rcx + ymm2 * 2], ymm3

Что касается реального использования для байтовой адресации, мы можем получить 24-битное цветное изображение, где каждый пиксель выровнен по 3 байта. Мы можем загрузить 8 пикселей с помощью одной инструкции VPGATHERDD, но только если поле "scale" в VSIB равно "1" и VPGATHERDD использует байтовую адресацию.

Судя по описанию в справочном документе по программированию Intel AVX, доступному здесь, похоже, что инструкции по сбору используют байтовую адресацию. В частности, см. Следующие цитаты из описания VPGATHERDD инструкция (на стр. 389):

DISP: optional 1, 2, 4 byte displacement;
DATA_ADDR = BASE_ADDR + (SignExtend(VINDEX[i+31:i])*SCALE + DISP;

Поскольку вы можете использовать смещения на 1/2/4 байта, я бы предположил, что общий адрес памяти является байтовым адресом. Хотя это может быть не обычное приложение, могут быть случаи, когда вы захотите прочитать 32- или 64-битное значение с неправильно выровненного адреса. Это одна из самых гибких вещей в архитектуре x86 по сравнению с чем-то вроде ARM; у вас есть возможность выполнять смещенные обращения, если вы хотите, вместо того, чтобы вызывать исключение ЦП, как делают некоторые другие.

почему вы хотите загружать с неправильно выровненных адресов (т. е. использовать масштаб < 4)?

Неверно выровненные нагрузки - не единственный вариант использования масштаба <размер элемента. У вас могут быть только индексы, которые предварительно масштабируются смещением байтов. Или рассмотрите возможность векторизации цикла по массиву указателей на структуры: вы можете собрать с базовым "адресом", равным нулю, или небольшим целочисленным смещением в структуре.

Поддержка этого варианта использования - одна из причин, по которой Intel должна разработать инструкцию asm для поддержки этого, поскольку сборки должны помочь компиляторам автоматически векторизовать больше кода. Также естественно, что байт VSIB очень близок к SIB в кодировке машинного кода, но они могли бы легко сместить коэффициент масштабирования, чтобы выбрать масштаб = 4,8,16,32 (или 8,16,32,64 для qword собирается) с полем 2-битной шкалы.

Коэффициенты масштабирования, превышающие размер элемента, во многих случаях также явно не полезны, и их легко эмулировать с помощью одной инструкции сдвига влево перед сборкой. Но было бы невозможно обойти запутанный масштабный коэффициент, поэтому использование немасштабированных индексов, несомненно, является более гибким выбором дизайна.


Другие варианты использования: сбор 16-битных элементов. Используйте 32-битный сбор и замаскируйте верхнюю половину каждого элемента после сбора. (Или просто оставьте это с мусором). Это приведет к смещенной загрузке, если какой-либо из ваших индексов будет нечетным (для масштабного коэффициента 2), поэтому он может быть медленным, если они пересекают границы 4 КБ (в отличие от истинного 16-битного набора).

Вы также можете представить себе использование сбора в качестве части функции декомпрессии, когда после некоторого декодирования у вас есть вектор смещений в буфере, и вам нужны произвольные 4-байтовые или 8-байтовые окна данных.

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