将字节打包成 u32 与将它们存储在 vec<u8> 中的性能差异?

Performance difference between bitpacking bytes into a u32 vs storing them in a vec<u8>?

简介:

我很好奇将小数字存储为位压缩无符号整数与字节向量的性能差异(cpu 和内存使用)

例子

我将使用存储 RGBA 值的示例。它们是 4 字节,因此很容易将它们存储为 u32
但是,将它们存储为 u8 类型的向量会更具可读性。


作为更详细的示例,假设我要存储和检索颜色 rgba(255,0,0,255)

这就是我执行这两种方法的方式:

// Bitpacked:
let i: u32 = 4278190335;
//binary is 11111111 00000000 00000000 11111111
//In reality I would most likely do something more similar to:
let i: u32 = 255 << 24 + 255; //i think this syntax is right

// Vector:
let v: Vec<u8> = [255,0,0,255];

然后可以用

查询两个红色值
i >> 24 
//or
&v[0]
//both expressions evaluate to 255 (i think. I'm really new to rust <3 )

问题 1

据我所知,v 的值必须存储在堆上,因此存在与之相关的性能成本。这些成本是否足以使比特打包值得?

问题 2

然后是i >> 24&v[0]这两个表达式。我不知道 rust 在位移位和从堆中获取值之间的速度有多快。我会测试它,但我暂时无法使用安装了 Rust 的机器。有人可以就这两种操作的缺点给出任何直接的见解吗?

问题 3

最后,内存使用的区别是否简单到只在堆栈上为 u32 存储 32 位与在堆栈上为指针 v 存储 64 位以及 32 位在 v?

的堆上

抱歉,如果这个问题有点令人困惑

使用Vec会更贵;正如您提到的,它将需要执行堆分配,并且还将对访问进行边界检查。

也就是说,如果您改用数组 [u8; 4],与位压缩 u32 表示相比的性能应该几乎相同。

事实上,请考虑以下简单示例:

pub fn get_red_bitpacked(i: u32) -> u8 {
    (i >> 24) as u8
}

pub fn get_red_array(v: [u8; 4]) -> u8 {
    v[3]
}

pub fn test_bits(colour: u8) -> u8 {
    let colour = colour as u32;
    let i = (colour << 24) + colour;
    get_red_bitpacked(i)
}

pub fn test_arr(colour: u8) -> u8 {
    let v = [colour, 0, 0, colour];
    get_red_array(v)
}

我查看了 Compiler Explorer,编译器认为 get_red_bitpackedget_red_array 完全相同:以至于它甚至都懒得为前者生成代码.这两个“测试”函数显然也针对完全相同的程序集进行了优化。

example::get_red_array:
        mov     eax, edi
        shr     eax, 24
        ret

example::test_bits:
        mov     eax, edi
        ret

example::test_arr:
        mov     eax, edi
        ret

显然这个例子被编译器看穿了:为了进行适当的比较,您应该以实际代码为基准。也就是说,我可以很安全地说,对于 Rust,u32[u8; 4] 对于这些类型的操作的性能通常应该是相同的。

tl;dr 使用结构:

struct Color {
    r: u8,
    g: u8,
    b: u8,
    a: u8,
}

也许也可以使用 repr(packed)

它为您提供世界上最好的东西,您可以为频道命名。

Are these costs significant enough to make bit packing worth it?

堆分配有巨大成本。

Are there any immediate insights someone could give on the drawbacks of these two operations?

与分配内存相比,两者都是噪音。