Ответ 1
update: Я вижу, что вы используете собственный векторный синтаксис GNU C, а не встроенные функции Intel. Вы избегаете встроенных функций Intel для переносимости на не-x86? gcc в настоящее время выполняет код компиляции с плохой работой, который использует векторы GNU C шире, чем поддерживает целевая машина. (Вы надеетесь, что он просто будет использовать два 128b вектора и работать по каждому отдельно, но, видимо, это хуже, чем это.)
В любом случае этот ответ показывает, как вы можете использовать встроенные функции Intel x86 для загрузки данных в векторные синтаксисы GNU C
Прежде всего, просмотр вывода компилятора менее чем -O2
- это пустая трата времени, если вы пытаетесь узнать что-либо о том, что будет скомпилировано для хорошего кода. Ваш main()
будет оптимизирован только для ret
на -O2.
Кроме того, не удивительно, что вы получаете плохие asm от назначения элементов вектора по одному.
Кроме того, обычные люди будут называть тип v4df
(вектор 4 Double Float) или что-то, а не vector
, поэтому они не сходят с ума при использовании его с С++ std::vector
. Для одной точности v8sf
. IIRC, gcc использует имена типов, подобные этому внутри, для __m256d
.
На x86 встроенные типы Intel (например, __m256d
) реализованы поверх векторного синтаксиса GNU C (поэтому вы можете сделать v1 * v2
в GNU C вместо записи _mm256_mul_pd(v1, v2)
). Вы можете свободно конвертировать из __m256d
в v4df
, как я сделал здесь.
Я обернул оба разумных способа сделать это в функциях, поэтому мы можем посмотреть их asm. Обратите внимание, как мы не загружаемся из массива, который мы определяем внутри одной и той же функции, поэтому компилятор не будет оптимизировать его.
Я помещал их в Godbolt explorer, чтобы вы могли посмотреть на asm с различными параметрами компиляции и версиями компилятора.
typedef double v4df __attribute__((vector_size(4 * sizeof(double))));
#include <immintrin.h>
// note the return types. gcc6.1 compiles with no warnings, even at -Wall -Wextra
v4df load_4_doubles_intel(const double *p) { return _mm256_loadu_pd(p); }
vmovupd ymm0, YMMWORD PTR [rdi] # tmp89,* p
ret
v4df avx_constant() { return _mm256_setr_pd( 1.0, 2.0, 3.0, 4.0 ); }
vmovapd ymm0, YMMWORD PTR .LC0[rip]
ret
Если аргументы args для _mm_set*
не являются константами времени компиляции, компилятор сделает все возможное, чтобы сделать эффективный код, чтобы все элементы были включены в один вектор. Обычно лучше всего это делать, а не писать C, который хранится в tmp-массиве и загружается из него, потому что это не всегда лучшая стратегия. (Ошибка хранилища в нескольких узких хранилищах, пересылаемых на широкую нагрузку, требует дополнительных ~ 10 циклов (IIRC) задержек в дополнение к обычной задержке пересылки. Если ваш double
уже находится в регистре, обычно лучше всего просто перемешайте их вместе.)
См. также Можно ли прикладывать float непосредственно к __m128, если они имеют 16 байтов с привязкой? для списка различных свойств для получения одного скаляра в вектор, x86 тег wiki имеет ссылки на руководства Intel, и их искатель intrinsics.
Загрузка/сохранение векторов GNU C без встроенных функций Intel:
Я не уверен, как вы "предположили" это сделать. Этот Q & A предлагает предлагать указатель на память, которую вы хотите загрузить, и использовать векторный тип типа typedef char __attribute__ ((vector_size (16),aligned (1))) unaligned_byte16;
(обратите внимание на атрибут aligned(1)
).
Вы получаете segfault от *(v4df *)a
, потому что предположительно a
не выравнивается по 32-байтовой границе, но вы используете векторный тип, который предполагает естественное выравнивание. (Точно так же, как __m256d
, если вы разыскиваете указатель на него, вместо того, чтобы использовать load/store intrinsics для передачи информации о выравнивании компилятору.)