TL; DR Встроенные функции AVX2 HW используются неправильно, что приводит к генерации очень неэффективного кода SIMD.
Ошибка заключается в том, что инструкции загружают, обрабатывают и хранят данныев буфере.Операция должна быть выполнена с использованием встроенных в память AVX / AVX2 Avx2.Xor, что ускорит время загрузки в 4 раза и вернет Vector256.Это, с другой стороны, вызовет Vector256.Create избыточный и ускорит выполнение.Наконец, данные должны храниться в массиве с помощью встроенного Avx2.Store ().Это снова ускорит код примерно в 4 раза.
Последняя оптимизация, которая должна быть применена, - это использование параллелизма на уровне команд ЦП.Обычно инструкции SIMD выполняются в течение заранее определенного числа циклов ЦП с задержкой, которая может превышать 1 цикл ЦП.Эти параметры зависят от процессора и могут быть найдены в:
Как и все оптимизации, которыемогут быть довольно сложными, я объясню их в более длинной записи чуть позже, но в целом я ожидаю ускорения до 4x из-за векторизации по сравнению с базовым сценарием для проблемы, над которой вы работаете.
КодПример, который вы используете, представляет собой простой цикл изменения данных в шагах с четырьмя беззнаковыми четырьмя словами и является идеальным кандидатом для автовекторизации за счет оптимизации компиляторов.Когда идентичный цикл C ++ оптимизирован с помощью GCC 9.1 с параметрами -O3 -march = haswell , результирующий машинный код показывает все стандартные оптимизации, примененные к циклу:
#include <cstdint>
void hash(uint64_t* buffer, uint64_t length) {
uint64_t* pBuffer = buffer;
const uint64_t CONST1 = 0x6753ul;
const uint64_t CONST2 = 0x7753ul;
const uint64_t CONST3 = 0x8753ul;
const uint64_t CONST4 = 0x9753ul;
for(uint64_t i = 0; i < length; i += 4)
{
*pBuffer ^= CONST1;
*(pBuffer + 1) ^= CONST2;
*(pBuffer + 2) ^= CONST3;
*(pBuffer + 3) ^= CONST4;
}
}
Godbolt CompilerРезультат проводника GCC 9.1
test rsi, rsi
je .L11
cmp rsi, -4
ja .L6
lea rdx, [rsi-1]
vmovdqa ymm1, YMMWORD PTR .LC0[rip]
xor eax, eax
shr rdx, 2
inc rdx
.L5:
vpxor ymm0, ymm1, YMMWORD PTR [rdi]
inc rax
add rdi, 32
vmovdqu YMMWORD PTR [rdi-32], ymm0
cmp rax, rdx
jb .L5
vzeroupper
.L11:
ret
.L6:
vmovdqa ymm1, YMMWORD PTR .LC0[rip]
xor eax, eax
.L3:
vpxor ymm0, ymm1, YMMWORD PTR [rdi]
add rax, 4
add rdi, 32
vmovdqu YMMWORD PTR [rdi-32], ymm0
cmp rsi, rax
ja .L3
vzeroupper
jmp .L11
.LC0:
.quad 26451
.quad 30547
.quad 34643
.quad 38739
Godbolt Результат проводника компилятора Clang 8.0
.LCPI0_0:
.quad 26451 # 0x6753
.quad 30547 # 0x7753
.quad 34643 # 0x8753
.quad 38739 # 0x9753
hash(unsigned long*, unsigned long): # @hash(unsigned long*, unsigned long)
test rsi, rsi
je .LBB0_3
xor eax, eax
vmovaps ymm0, ymmword ptr [rip + .LCPI0_0] # ymm0 = [26451,30547,34643,38739]
.LBB0_2: # =>This Inner Loop Header: Depth=1
vxorps ymm1, ymm0, ymmword ptr [rdi + 8*rax]
vmovups ymmword ptr [rdi + 8*rax], ymm1
add rax, 4
cmp rax, rsi
jb .LBB0_2
.LBB0_3:
vzeroupper
ret