Выровненный и невыровненный доступ к памяти с помощью встроенных функций 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)?