Предложение "Предположим" в gcc
Имеет ли gcc (последние версии: 4.8, 4.9) предложение "предположить", подобное __assume()
встроенный поддерживается ICC? Например, __assume( n % 8 == 0 );
3 ответа
В вашем примере вы хотите сообщить компилятору, что N
кратно 8. Вы можете сделать это, просто вставив строку
N = N & 0xFFFFFFF8;
в вашем коде (если N
является 32-разрядным целым числом). Это не меняет N
, так как N
кратно 8, но, начиная с GCC 4.9, компилятор, похоже, понимает, что N
кратно 8, после этой строки.
Это показано на следующем примере, в котором добавлены два вектора с плавающей точкой:
int add_a(float * restrict a, float * restrict b, int N)
{
a = (float*)__builtin_assume_aligned(a, 32);
b = (float*)__builtin_assume_aligned(b, 32);
N = N & 0xFFFFFFF8;
for (int i = 0; i < N; i++){
a[i] = a[i] + b[i];
}
return 0;
}
int add_b(float * restrict a, float * restrict b, int N)
{
a = (float*)__builtin_assume_aligned(a, 32);
b = (float*)__builtin_assume_aligned(b, 32);
for (int i = 0; i < N; i++){
a[i] = a[i] + b[i];
}
return 0;
}
С gcc -m64 -std=c99 -O3
, gcc версия 4.9, add_a
компилируется в векторизованный код
add_a:
and edx, -8
jle .L6
sub edx, 4
xor ecx, ecx
shr edx, 2
lea eax, [rdx+1]
xor edx, edx
.L3:
movaps xmm0, XMMWORD PTR [rdi+rdx]
add ecx, 1
addps xmm0, XMMWORD PTR [rsi+rdx]
movaps XMMWORD PTR [rdi+rdx], xmm0
add rdx, 16
cmp ecx, eax
jb .L3
.L6:
xor eax, eax
ret
С функцией add_b
более 20 дополнительных инструкций необходимы для решенияN
не кратно 8:
add_b:
test edx, edx
jle .L17
lea ecx, [rdx-4]
lea r8d, [rdx-1]
shr ecx, 2
add ecx, 1
cmp r8d, 2
lea eax, [0+rcx*4]
jbe .L16
xor r8d, r8d
xor r9d, r9d
.L11:
movaps xmm0, XMMWORD PTR [rdi+r8]
add r9d, 1
addps xmm0, XMMWORD PTR [rsi+r8]
movaps XMMWORD PTR [rdi+r8], xmm0
add r8, 16
cmp ecx, r9d
ja .L11
cmp eax, edx
je .L17
.L10:
movsx r8, eax
lea rcx, [rdi+r8*4]
movss xmm0, DWORD PTR [rcx]
addss xmm0, DWORD PTR [rsi+r8*4]
movss DWORD PTR [rcx], xmm0
lea ecx, [rax+1]
cmp edx, ecx
jle .L17
movsx rcx, ecx
add eax, 2
lea r8, [rdi+rcx*4]
cmp edx, eax
movss xmm0, DWORD PTR [r8]
addss xmm0, DWORD PTR [rsi+rcx*4]
movss DWORD PTR [r8], xmm0
jle .L17
cdqe
lea rdx, [rdi+rax*4]
movss xmm0, DWORD PTR [rdx]
addss xmm0, DWORD PTR [rsi+rax*4]
movss DWORD PTR [rdx], xmm0
.L17:
xor eax, eax
ret
.L16:
xor eax, eax
jmp .L10
Смотрите ссылку Godbolt.
Начиная с gcc 4.8.2, в gcc нет эквивалента __assume(). Я не знаю почему - это было бы очень полезно. мафсо предложил:
#define __assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
Это старый трюк, известный по крайней мере еще в 2010 году и, вероятно, дольше. Компилятор обычно оптимизирует оценку 'cond', потому что любая оценка, для которой cond имеет значение false, в любом случае будет неопределенной. Тем не менее, похоже, что он не оптимизирует "cond", если он содержит вызов непрозрачной (не встроенной) функции. Компилятор должен предположить, что непрозрачный вызов может иметь побочный эффект (например, изменить глобальный) и не может оптимизировать вызов, хотя он может оптимизировать любые вычисления и ответвления в результате. По этой причине макроподход в лучшем случае является частичным решением.
[[assume(...)]];
- в C++23 добавлена переносимая версия.
__attribute__((__assume__(...)));
- добавлено в GCC 13 вместе с вышеперечисленным, полезно для кода C.