Ответ 1
В SSE существуют явные и неявные нагрузки.
-
_mm_load_si128(reinterpret_cast<__m128i*>(&cd->data[idx]));
- явная загрузка -
*reinterpret_cast<__m128i*>(&cd->data[idx]);
- неявная загрузка
При явной загрузке вы явно инструктируете компилятор загружать данные в регистр XMM - это "официальный" способ Intel для этого. Вы также можете контролировать, является ли нагрузка выравниваемой или невыровненной нагрузкой, используя _mm_load_si128
или _mm_loadu_si128
.
Хотя в качестве расширения большинство компиляторов также могут автоматически генерировать нагрузки XMM, когда вы выполняете type-punning, но таким образом вы не можете контролировать выравнивание или выравнивание нагрузки. В этом случае, поскольку на современных процессорах нет ограничений производительности при использовании невыровненных нагрузок при выравнивании данных, компиляторы обычно используют универсальные нагрузки повсеместно.
Другой, более важный аспект заключается в том, что при неявных нагрузках вы нарушаете правила strict aliasing, что может привести к поведению undefined. Хотя стоит упомянуть, что - как часть расширения - компиляторы, поддерживающие Intel, не склонны применять строгие правила псевдонимов для типов заполнителей XMM, таких как __m128
, __m128d
, __m128i
.
Тем не менее, я считаю, что явные нагрузки более чистые и более пуленепробиваемые.
Почему компиляторы не склонны применять строгие правила псевдонимов для типов заполнителей SSE?
1-я причина заключается в разработке свойств SSE: есть очевидные случаи, когда вам нужно использовать пульт типа, поскольку нет другого способа использовать некоторые из встроенных функций. Мистический ответ прекрасно описывает его.
Как отметил Коди Грей в комментариях, стоит упомянуть, что исторически instrinsics MMX (которые в настоящее время в основном заменяются SSE2) даже не предоставляют явные нагрузки или хранилища - вам приходилось использовать пул типа.
Вторая причина (несколько связанная с первой) заключается в определениях типов этих типов.
GCC typedef
для типов заполнителей SSE/SSE2 в <xmmintrin.h >
и <emmintrin.h>
:
/* The Intel API is flexible enough that we must allow aliasing with other
vector types, and their scalar components. */
typedef float __m128 __attribute__ ((__vector_size__ (16), __may_alias__));
typedef long long __m128i __attribute__ ((__vector_size__ (16), __may_alias__));
typedef double __m128d __attribute__ ((__vector_size__ (16), __may_alias__));
Ключевым здесь является атрибут __may_alias__
, который делает работу по типу записи на этих типах, даже если строгий псевдоним включен с флагом -fstrict-aliasing
.
Теперь, поскольку clang и ICC совместимы с GCC, они должны следовать тому же соглашению. Так что в настоящее время в этих 3 компиляторах неявные нагрузки/хранилища несколько гарантированно работают даже с флагом -fstrict-aliasing
. Наконец, MSVC не поддерживает строгий псевдонимы вообще, поэтому он даже не может быть проблемой.
Тем не менее, это не означает, что вы должны предпочесть неявные нагрузки/хранилища по явным.