- Всегда ли это приводит к правильному выводу (при условии, что переполнения нет)?
Да.При условии, что каждая сумма меньше 256, это добавит байты, как вы хотите.Вы указали "ne" в каждом случае для нативного порядка байтов.Это будет работать независимо от собственного порядка байтов, поскольку операции выполняются побайтово.
Если бы вы написали код для проверки того, что все суммы находятся в диапазоне, то вы почти наверняка отменили бы любое дополнительное ускорение, которое у вас было (если оно было для начала).
Это быстрее, чем просто делать дополнения по отдельности?
Может быть.Единственный способ узнать наверняка - это проверить.
Верно ли это для других целочисленных типов?Например, 2 u16
s в u32
добавлено с 2 другими u16
s в u32
?
Да, но вы должны обратить внимание на порядок байтов.
Если это существует и является общим, как оно называется?
Это не распространено, потому что обычно это не нужно.Этот тип оптимизации усложняет чтение кода и создает значительные сложности и возможности для ошибок.Компилятор Rust и LLVM между ними способны находить чрезвычайно сложные оптимизации, о которых вы никогда не думаете, в то время как ваш код остается читабельным и обслуживаемым.
Если у него есть имя, это SIMD и поддержка большинства современных процессоров.форма его изначально (SSE, MMX, AVX).Вы можете сделать это вручную, используя встроенные функции, например, core::arch::x86_64::_mm_add_epi8
, но LLVM может сделать это автоматически.Возможно, что попытка сделать это вручную может помешать оптимизации, которую в противном случае сделала бы LLVM, и в то же время сделать ваш код более подверженным ошибкам.
Я не эксперт по ассемблерному коду.любым способом, но я взглянул на сборку, сгенерированную для следующих двух функций:
#[no_mangle]
#[inline(never)]
pub fn f1(a1: u8, b1: u8, c1: u8, d1: u8, a2: u8, b2: u8, c2: u8, d2: u8) -> [u8; 4]{
let a = u32::from_le_bytes([a1, b1, c1, d1]);
let b = u32::from_le_bytes([a2, b2, c2, d2]);
u32::to_le_bytes(a + b)
}
#[no_mangle]
#[inline(never)]
pub fn f2(a1: u8, b1: u8, c1: u8, d1: u8, a2: u8, b2: u8, c2: u8, d2: u8) -> [u8; 4]{
[a1 + a2, b1 + b2, c1 + c2, d1 + d2]
}
сборка для f1
:
movzx r10d, byte ptr [rsp + 8]
shl ecx, 24
movzx eax, dl
shl eax, 16
movzx edx, sil
shl edx, 8
movzx esi, dil
or esi, edx
or esi, eax
or esi, ecx
mov ecx, dword ptr [rsp + 16]
shl ecx, 24
shl r10d, 16
movzx edx, r9b
shl edx, 8
movzx eax, r8b
or eax, edx
or eax, r10d
or eax, ecx
add eax, esi
ret
А для f2
:
add r8b, dil
add r9b, sil
add dl, byte ptr [rsp + 8]
add cl, byte ptr [rsp + 16]
movzx ecx, cl
shl ecx, 24
movzx edx, dl
shl edx, 16
movzx esi, r9b
shl esi, 8
movzx eax, r8b
or eax, esi
or eax, edx
or eax, ecx
ret
Меньшее количество инструкций не обязательно делает это быстрее, но это неплохой ориентир.
Рассматривайте этот вид оптимизации в качестве последнего средства, после тщательных измерений и испытаний.