math - 通过将它们转换为更大的整数数据类型来一次添加整个字节数组是否有效?

标签 math optimization rust

如果我有两个包含 u8 的数组,我可以将它们转换为更大的整数类型以减少我需要做的加法次数吗?例如,如果两个字节数组每个包含 4 个字节,我可以将它们每个都变成一个 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. 它是否适用于其他整数类型?例如 u32 中的 2 个 u16 添加了 u32 中的另外 2 个 u16

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

最佳答案

  1. Does this always result in the right output (assuming there is no overflow)?

是的。如果每个总和小于 256,这将根据需要添加字节。您在每种情况下都指定了“ne”,以实现 native 字节序。这将起作用,无论 native 字节序如何,因为操作是按字节进行的。

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

  1. Is this faster than just doing the additions individually?

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

  1. Does it hold true for other integer types? Such as 2 u16s in a u32 added with 2 other u16s in a u32?

可以,但是需要注意字节序。

If this exists and is common, what is it called?

这并不常见,因为它通常是不必要的。这种类型的优化使代码更难阅读,并引入相当大的复杂性和错误机会。 Rust 编译器和它们之间的 LLVM 能够找到您永远不会想到的极其复杂的优化,同时您的代码保持可读性和可维护性。

如果它有一个名字,那就是 SIMD,大多数现代处理器本身都支持它的一种形式(SSE、MMX、AVX)。您可以使用内置函数手动执行此操作,例如core::arch::x86_64::_mm_add_epi8 ,但 LLVM 可能会自动执行。尝试手动执行此操作可能会干扰 LLVM 否则会进行的优化,同时使您的代码更容易出错。


无论如何我都不是汇编代码专家,但我看了一下 assembly generated对于以下两个函数:

#[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

较少的指令不一定会使它更快,但这不是一个坏的准则。


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

关于math - 通过将它们转换为更大的整数数据类型来一次添加整个字节数组是否有效?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58000264/

相关文章:

performance - 我应该如何有效地初始化 `Arc<[u8; 65536]>`?

types - async fn 的类型是什么?

java - 使用 Random 类 "unnecessarily complicate"有用吗?

java - 在不计算垂直 vector 的情况下获取二维三角形中点的距离?

c# - C#(或类似语言)中的 Atan2

javascript - 调整旋转元素大小时计算正确的宽度和高度

optimization - 快速计算 3D 数组中相邻点的方法

MySql 查询不使用索引集

mysql - 在mysql中使用日期类型字段优化查询

rust - 正确拆分时,Rust不允许可变借用