首页 > 解决方案 > 通过将它们转换为更大的整数数据类型来一次添加整个字节数组是否有效?

问题描述

如果我有两个包含u8s 的数组,我可以将它们转换为更大的整数类型以减少我需要做的加法次数吗?例如,如果两个字节数组每个包含 4 个字节,我可以将它们分别变成 a u32,进行加法,然后将它们转换回来吗?

例如:

let a = u32::from_ne_bytes([1, 2, 3, 4]);
let b = u32::from_ne_bytes([5, 6, 7, 8]);

let c = a + b;
let c_bytes = u32::to_ne_bytes(c);

assert_eq!(c_bytes, [6, 8, 10, 12]);

此示例产生正确的输出。

  1. 这是否总是导致正确的输出(假设没有溢出)?
  2. 这比单独添加要快吗?
  3. 它适用于其他整数类型吗?比如 a 中的 2 u16su32加上 a 中的 2 other u16s u32

如果这存在并且很常见,它叫什么?

标签: mathoptimizationrust

解决方案


  1. 这是否总是导致正确的输出(假设没有溢出)?

是的。如果每个总和小于 256,这将根据需要添加字节。您在每种情况下都指定了“ne”,用于本地字节序。无论本机字节顺序如何,这都会起作用,因为操作是按字节计算的。

如果您编写代码来实际检查总和是否都在范围内,那么您几乎肯定会撤消您获得的任何额外加速(如果有任何开始的话)。

  1. 这比单独添加要快吗?

也许。唯一确定的方法是测试。

  1. 它适用于其他整数类型吗?比如 a 中的 2 u16su32加上 a 中的 2 other u16s 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

更少的指令并不一定会让它更快,但这不是一个糟糕的指导方针。


在仔细测量和测试之后,将这种优化视为最后的手段。


推荐阅读