Alignment und SSE seltsames Verhalten
Ich versuche, mit SSE zu arbeiten, und habe mich mit einem merkwürdigen Verhalten konfrontiert.
Ich schreibe einfachen Code zum Vergleichen zweier Zeichenfolgen mit SSE Intrinsics, führe ihn aus und es funktioniert. Aber später verstehe ich, dass in meinem Code einer der Zeiger noch nicht ausgerichtet ist, aber ich benutze_mm_load_si128
-Anweisung, bei der der Zeiger an einer 16-Byte-Grenze ausgerichtet sein muss.
//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;
}
Ich drucke Ausrichtung von zwei Zeigern und einer dieser Wal ausgerichtet, aber an zweiter Stelle - war nicht. Und das Programm läuft immer noch richtig und schnell.
Dann erstelle ich synthetischen Test wie folgt:
//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);
}
Und es stürzt erwartungsgemäß bei der ersten Iteration ab, wenn Zeiger B geladen wird.
Interessante Tatsache, dass, wenn ich @ wechsetarget
zusse4.2
dann meine Implementierung vonis_equal
wird abstürzen.
Eine weitere interessante Tatsache, dass, wenn ich versuche, den zweiten Zeiger anstelle des ersten auszurichten (so dass der erste Zeiger nicht ausgerichtet wird, der zweite Zeiger ausgerichtet), dannis_equal
wird abstürzen.
Also, meine Frage ist: "Warumis_equal
-Funktion funktioniert gut mit nur ersten Zeiger ausgerichtet, wenn ich @ aktivieravx
Anweisungsgenerierung? "
UPD: Das istC++
code. Ich kompiliere meinen Code mitMinGW64/g++, gcc version 4.9.2
unter Windows, x86.
Compile string:g++.exe main.cpp -Wall -Wextra -std=c++11 -O2 -Wcast-align -Wcast-qual -o main.exe