Выравнивание и странное поведение SSE
Я пытаюсь работать с SSE, и я столкнулся с некоторым странным поведением.
Я пишу простой код для сравнения двух строк с SSE Intrinsics, запускаю его, и он работает. Но позже я понимаю, что в моем коде один из указателей все еще не выровнен, но я использую_mm_load_si128
инструкция, которая требует, чтобы указатель был выровнен по 16-байтовой границе.
//Compare two different, not overlapping piece of memory
__attribute((target("avx"))) int is_equal(const void* src_1, const void* src_2, size_t size)
{
//Skip tail for right alignment of pointer [head_1]
const char* head_1 = (const char*)src_1;
const char* head_2 = (const char*)src_2;
size_t tail_n = 0;
while (((uintptr_t)head_1 % 16) != 0 && tail_n < size)
{
if (*head_1 != *head_2)
return 0;
head_1++, head_2++, tail_n++;
}
//Vectorized part: check equality of memory with SSE4.1 instructions
//src1 - aligned, src2 - NOT aligned
const __m128i* src1 = (const __m128i*)head_1;
const __m128i* src2 = (const __m128i*)head_2;
const size_t n = (size - tail_n) / 32;
for (size_t i = 0; i < n; ++i, src1 += 2, src2 += 2)
{
printf("src1 align: %d, src2 align: %d\n", align(src1) % 16, align(src2) % 16);
__m128i mm11 = _mm_load_si128(src1);
__m128i mm12 = _mm_load_si128(src1 + 1);
__m128i mm21 = _mm_load_si128(src2);
__m128i mm22 = _mm_load_si128(src2 + 1);
__m128i mm1 = _mm_xor_si128(mm11, mm21);
__m128i mm2 = _mm_xor_si128(mm12, mm22);
__m128i mm = _mm_or_si128(mm1, mm2);
if (!_mm_testz_si128(mm, mm))
return 0;
}
//Check tail with scalar instructions
const size_t rem = (size - tail_n) % 32;
const char* tail_1 = (const char*)src1;
const char* tail_2 = (const char*)src2;
for (size_t i = 0; i < rem; i++, tail_1++, tail_2++)
{
if (*tail_1 != *tail_2)
return 0;
}
return 1;
}
Я печатаю выравнивание двух указателей и одного из этих выравниваний, но второго - нет. И программа по-прежнему работает правильно и быстро.
Затем я создаю синтетический тест, как это:
//printChars128(...) function just print 16 byte values from __m128i
const __m128i* A = (const __m128i*)buf;
const __m128i* B = (const __m128i*)(buf + rand() % 15 + 1);
for (int i = 0; i < 5; i++, A++, B++)
{
__m128i A1 = _mm_load_si128(A);
__m128i B1 = _mm_load_si128(B);
printChars128(A1);
printChars128(B1);
}
И он вылетает, как мы и ожидали, на первой итерации при попытке загрузить указатель B.
Интересный факт, что если я переключусьtarget
вsse4.2
тогда моя реализацияis_equal
потерпит крах
Еще один интересный факт: если я попытаюсь выровнять второй указатель вместо первого (таким образом, первый указатель будет не выровнен, второй - выровнен), тоis_equal
потерпит крах
Итак, мой вопрос: «Почемуis_equal
функция работает нормально только с первым указателем, если я включилavx
генерация инструкций?
UPD: ЭтоC++
код. Я компилирую свой код сMinGW64/g++, gcc version 4.9.2
под виндой х86.
Строка компиляции:g++.exe main.cpp -Wall -Wextra -std=c++11 -O2 -Wcast-align -Wcast-qual -o main.exe