__m128d
- это тип, который предполагает / требует / гарантирует (компилятору) 16-байтовое выравнивание 1 .
Приведение неверно выровненного указателя к __m128d*
и разыменование егонеопределенное поведение, и это ожидаемый результат. Используйте _mm_loadu_pd
, если ваши данные могут быть не выровнены. (Или, предпочтительно, выровняйте ваши данные с alignas(16) double a[bufferSize];
2 ).ISO C ++ 11 и более поздние версии имеют переносимый синтаксис для выравнивания статического и автоматического хранения (но не так просто для динамического хранения).
Приведение указателя к __m128d*
и разыменование его похоже на обещание компилятору, что он выровнен. C ++ позволяет вам лгать компилятору с потенциально катастрофическими результатами. Выполнение операции, требующей выравнивания, не приводит к обратному выравниванию ваших данных;это не имеет смысла или даже невозможно, когда вы компилируете несколько файлов по отдельности или когда вы работаете с помощью указателей.
Сноска 1. Интересный факт: реализация встроенного в Intel API встроенных функций Intel добавляет тип __m128d_u
: невыровненные векторы, которые подразумевают 1-байтовое выравнивание, если вы разыменовываете указатель.
typedef double __m128d_u
__attribute__ ((__vector_size__ (16), __may_alias__, __aligned__ (1)));
Не использовать в переносимом коде;Я не думаю, что MSVC поддерживает это, а Intel не определяет это.
Сноска 2: В вашем случае вам также необходимо выровнять каждую строку ваших 2D-массивов на 16Поэтому вам нужно, чтобы размерность массива была [voiceSize][round_up_to_next_power_of_2(bufferSize)]
, если bufferSize
может быть нечетным.Оставление неиспользованных дополнительных элементов в конце каждой строки является обычной техникой, например, в графическом программировании для двумерных изображений с потенциально нечетной шириной.
Кстати, это не "особый""или специфично для встроенных функций: приведение void*
или char*
к int*
(и разыменование его) безопасно только в том случае, если оно достаточно выровнено. В x86-64 System V и Windows x64, alignof(int) = 4
.
(Интересный факт: даже создание неправильно выровненного указателя - это неопределенное поведение в ISO C ++. Но компиляторы, которые поддерживают встроенный API-интерфейс Intel, должны поддерживать такие вещи, как _mm_loadu_si128( (__m128i*)char_ptr )
, поэтому мы можем рассмотреть создание без разыменования невыровненных указателей как частьрасширение.)
Обычно это работает на x86, потому что только 16-байтовые загрузки имеют версию, требующую выравнивания.Но на SPARC, например, у вас потенциально может быть та же проблема.Тем не менее, можно столкнуться с проблемами со смещенными указателями на int
или short
даже на x86. Почему при выравнивании доступа к памяти mmap иногда происходит ошибка segfault на AMD64? является хорошим примером: автоматическая векторизация с помощью gcc предполагает, что некоторое целое число элементов uint16_t
достигнет 16-байтовой границы выравнивания.
Также проще столкнуться с проблемами встроенных функций, поскольку alignof(__m128d)
больше, чем выравнивание большинства примитивных типов.В 32-битных реализациях x86 C ++ alignof(maxalign_t)
- только 8, поэтому malloc
и new
обычно возвращают только 8-байтовую выровненную память.