Выбор между выровненными и несогласованными инструкциями 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, чтобы получить результат в регистре для повторного использования.)