Почему бы вам не получить доступ к полям __m128i напрямую?

Я читал это в MSDN, и там написано

Вы не должны обращаться к полям __m128i напрямую. Однако вы можете увидеть эти типы в отладчике. Переменная типа __m128i сопоставляется с регистрами XMM[0-7].

Однако не объясняет, почему. Почему это? Например, является ли следующее «плохим»:

void func(unsigned short x, unsigned short y)
{
    __m128i a;
    a.m128i_i64[0] = x;

    __m128i b;
    b.m128i_i64[0] = y;

    // Now do something with a and b ...
}

Вместо выполнения присваиваний, как в приведенном выше примере, следует ли использовать какую-то функцию load?


person Gideon    schedule 04.04.2014    source источник
comment
Поля специфичны для Microsoft. Конечно, им на это наплевать, так как они любят запирать вас в своем компиляторе. Настоящая причина заключается в производительности. Не существует эффективного способа доступа к отдельным элементам регистра SSE. В SSE4.1 есть инструкции для этого, но индекс должен быть константой времени компиляции.   -  person Mysticial    schedule 04.04.2014


Ответы (1)


Поле m128i_i64 и семейство являются специфическими расширениями компилятора Microsoft. Их нет в большинстве других компиляторов.

Тем не менее, они полезны для целей тестирования.


Настоящая причина избегать их использования — производительность. Аппаратное обеспечение не может эффективно обращаться к отдельным элементам вектора SIMD.

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

AVX и AVX2 не расширяют инструкции SSE4.1, чтобы разрешить доступ к элементам в 256-битном векторе. И, насколько я могу судить, у AVX512 его не будет для 512-битных векторов.

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


Возникает вопрос: Есть ли эффективный способ заполнения SIMD-вектора скалярными компонентами? (или разделить SIMD-вектор на скалярные компоненты)

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

person Mysticial    schedule 04.04.2014
comment
Приятно снова увидеть ответ от вас Mystical на SIMD. Вики-ссылка на переадресацию магазина интересна. - person Z boson; 04.04.2014
comment
Ага. Store-forwarding — довольно большая проблема для современных процессоров. Без этого вы платите штрафы в 20+ циклов за чтение после записи. К сожалению, это имеет тенденцию к сбою, когда вы пытаетесь прочитать память, используя другой размер, в который она была записана. Новые процессоры лучше тем, что вы можете читать, если они полностью содержатся в ожидающей записи. Но внутренние свойства множества идут другим путем. И единицы хранения в настоящее время не способны объединить меньшие магазины в один большой, чтобы его можно было перенаправить на более крупную загрузку. - person Mysticial; 04.04.2014
comment
Спасибо! Итак, в моем примере кода, как следует загружать аргументы в типы __m128i? Из некоторых других вопросов я вижу, как это сделать с массивами. Однако загрузка только простого целого числа, кажется, дает мне нарушение прав доступа. Вероятно, это проблема выравнивания, но я не уверен, как это исправить не для MS... - person Gideon; 05.04.2014
comment
@user3475799 user3475799 Если вам нужно загрузить SIMD-тип из разных скалярных источников, лучше всего это сделать с помощью встроенных функций. Компилятор (обычно) выберет арендодателя зол и сгенерирует самый быстрый код. Кроме того, он не должен падать. Компилятор должен автоматически выравнивать __m128i, если он находится в стеке. Если вы выделяете его в куче, то это другая история. - person Mysticial; 06.04.2014