Выровненный и невыровненный доступ к памяти с помощью встроенных функций AVX / AVX2

Согласно Руководству разработчика программного обеспечения Intel (раздел 14.9), AVX ослабил требования к выравниванию обращений к памяти. Если данные загружаются непосредственно в инструкцию обработки, например,

vaddps ymm0,ymm0,YMMWORD PTR [rax]

адрес загрузки не должен быть выровнен. Однако, если используется специальная выровненная инструкция загрузки, такая как

vmovaps ymm0,YMMWORD PTR [rax]

адрес загрузки должен быть выровнен (кратно 32), в противном случае возникает исключение.

Что меня смущает, так это автоматическая генерация кода из встроенных функций, в моем случае это gcc / g ++ (4.6.3, Linux). Пожалуйста, взгляните на следующий тестовый код:

#include <x86intrin.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#define SIZE (1L << 26)
#define OFFSET 1

int main() {
  float *data;
  assert(!posix_memalign((void**)&data, 32, SIZE*sizeof(float)));
  for (unsigned i = 0; i < SIZE; i++) data[i] = drand48();
  float res[8]  __attribute__ ((aligned(32)));
  __m256 sum = _mm256_setzero_ps(), elem;
  for (float *d = data + OFFSET; d < data + SIZE - 8; d += 8) {
    elem = _mm256_load_ps(d);
    // sum = _mm256_add_ps(elem, elem);
    sum = _mm256_add_ps(sum, elem);
  }
  _mm256_store_ps(res, sum);
  for (int i = 0; i < 8; i++) printf("%g ", res[i]); printf("\n");
  return 0;
}

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

Я компилирую код с

g++ -Wall -O3 -march=native -o memtest memtest.C

на процессоре с AVX. Если я проверю код, сгенерированный g ++ с помощью

objdump -S -M intel-mnemonic memtest | more

Я вижу, что компилятор не генерирует выровненную инструкцию загрузки, но загружает данные непосредственно в инструкцию сложения векторов:

vaddps ymm0,ymm0,YMMWORD PTR [rax]

Код выполняется без каких-либо проблем, даже если адреса памяти не выровнены (OFFSET равен 1). Это понятно, так как vaddps допускает невыровненные адреса.

Если я раскомментирую строку со вторым внутренним сложением, компилятор не сможет объединить нагрузку и сложение, поскольку vaddps может иметь только один операнд источника памяти и генерирует:

vmovaps ymm0,YMMWORD PTR [rax]
vaddps ymm1,ymm0,ymm0
vaddps ymm0,ymm1,ymm0

И теперь в программе происходит сбой, поскольку используется выделенная выровненная инструкция загрузки, но адрес памяти не выровнен. (Программа не вызывает ошибку, если я использую _mm256_loadu_ps, или если я установил OFFSET в 0, кстати.)

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

Мой вопрос: есть ли способ заставить компилятор C либо сгенерировать прямую загрузку в инструкции обработки (например, vaddps), либо сгенерировать выделенную инструкцию загрузки (например, vmovaps)?

Ответы на вопрос(2)

Ваш ответ на вопрос