Выбор между выровненными и несогласованными инструкциями x86 SIMD

существует, как правило, два типа инструкций SIMD:

A. те, которые работают с выровненными адресами памяти, которые вызовут исключение общей защиты (#GP), если адрес не выровнен по границе размера операнда:

movaps  xmm0, xmmword ptr [rax]
vmovaps ymm0, ymmword ptr [rax]
vmovaps zmm0, zmmword ptr [rax]

B. И те, которые работают с несогласованными адресами памяти, которые не вызовут такого исключения:

movups  xmm0, xmmword ptr [rax]
vmovups ymm0, ymmword ptr [rax]
vmovups zmm0, zmmword ptr [rax]

но мне просто любопытно, почему я хочу застрелиться в ногу и использования соответствие инструкции памяти из первая группа вообще?

2 ответов


  • Unaligned access: только movups/vmovups можно использовать. Здесь также применяются те же наказания, что и в случае выровненного доступа (см. Далее). Кроме того, доступы, которые пересекают линию кэша или границу виртуальной страницы, всегда несут штраф на всех процессорах.
  • невыравненный доступ:
    • на Intel Nehalem и позже (включая Silvermont и позже) и AMD Bulldozer и позже: после predecoding они выполняются одинаково точно для тех же операндов. Это включает поддержка устранения перемещения. Для этапов fetch и predecode они потребляют одинаковые точные ресурсы для одних и тех же операндов.
    • на pre-Nehalem и Bonnell и pre-Bulldozer: они декодируются в разные плавленые Домены uops и unfused domain uops. movups/vmovups потребляйте больше ресурсов (до двух раз больше) в интерфейсе и бэкэнде конвейера. Другими словами,movups/vmovups может быть в два раза медленнее, чем movaps/vmovaps С точки зрения задержки и/или пропускная способность.

поэтому, если вы не заботитесь о более старых микроархитектурах, оба технически эквивалентны. Хотя, если вы знаете или ожидаете, что данные будут выровнены, вы должны использовать выровненные инструкции, чтобы убедиться, что данные действительно выровнены без добавления явных проверок в коде.


я думаю, что есть тонкая разница между использованием _mm_loadu_ps и _mm_load_ps даже на "Intel Nehalem и позже (включая Silvermont и позже) и AMD Bulldozer и позже", которые могут повлиять на производительность.

операции, которые складывают нагрузку и другую операцию, такую как умножение в одну инструкцию, могут быть выполнены только с load, а не loadu intrinsics, если вы не компилируете с включенным AVX, чтобы разрешить несогласованные операнды памяти.

рассмотреть следующий код

#include <x86intrin.h>
__m128 foo(float *x, float *y) {
    __m128 vx = _mm_loadu_ps(x);
    __m128 vy = _mm_loadu_ps(y);
    return vx*vy;
}

становится преобразовано в

movups  xmm0, XMMWORD PTR [rdi]
movups  xmm1, XMMWORD PTR [rsi]
mulps   xmm0, xmm1

однако, если выровненная нагрузка встроена (_mm_load_ps) используются, он скомпилирован в

movaps  xmm0, XMMWORD PTR [rdi]
mulps   xmm0, XMMWORD PTR [rsi]

что сохраняет одну инструкцию. Но если компилятор может использовать закодированные нагрузки VEX, это только две инструкции для unaligned, а также.

vmovups xmm0, XMMWORD PTR [rsi]
vmulps  xmm0, xmm0, XMMWORD PTR [rdi]

для выровненного доступа, хотя нет никакой разницы в производительности при использовании инструкций movaps и movups на Intel Nehalem и позже или Silvermont и позже, или AMD бульдозер и позже.

а там can разница в производительности при использовании _mm_loadu_ps и _mm_load_ps встроенные функции при компиляции без AVX включен, в случаях, когда компромисс компилятора не movaps и movups, между movups или складывание груза в инструкцию ALU. (Что происходит, когда вектор используется только как вход для одной вещи, в противном случае компилятор будет использовать mov* load, чтобы получить результат в регистре для повторного использования.)