您可能在Linux上,那里的默认分配器很好(我在Windows上进行了基准测试,将分配器替换为
mimalloc
我得到了同样的结果)。
这是因为数组版本需要对每个元素进行边界检查,这会阻止向量化并增加开销。但他们不必这样做:如果我们制作参数数组而不是切片,那么边界检查就会消失。
pub fn slot_key_array_arrays(address: &[u8; 20], slot: &[u8; 32]) -> [u8; 53] {
// ...
}
如果只有切片,则可以使用将它们转换为数组
try_into()
:
#[inline]
pub fn slot_key_copy_array_try_into(address: &[u8], slot: &[u8]) -> [u8; 53] {
slot_key_array_arrays(address.try_into().unwrap(), slot.try_into().unwrap())
}
另一种选择是使用
copy_from_slice()
:
#[inline]
pub fn slot_key_copy_from_slice(address: &[u8], slot: &[u8]) -> [u8; 53] {
let mut key: [u8; 53] = [0; 53];
key[0] = DB_PREFIX_SLOT;
key[1..][..20].copy_from_slice(address);
key[21..].copy_from_slice(slot);
key
}
基准
copy_from_slice time: [6.8321 ns 6.8841 ns 6.9611 ns]
Found 13 outliers among 100 measurements (13.00%)
4 (4.00%) high mild
9 (9.00%) high severe
array_try_into time: [4.6906 ns 4.7001 ns 4.7119 ns]
Found 11 outliers among 100 measurements (11.00%)
3 (3.00%) high mild
8 (8.00%) high severe
array_arrays time: [4.8212 ns 4.8325 ns 4.8473 ns]
Found 15 outliers among 100 measurements (15.00%)
4 (4.00%) high mild
11 (11.00%) high severe
array time: [12.963 ns 12.992 ns 13.029 ns]
Found 17 outliers among 100 measurements (17.00%)
5 (5.00%) high mild
12 (12.00%) high severe
sized time: [10.116 ns 10.218 ns 10.327 ns]
Found 4 outliers among 100 measurements (4.00%)
2 (2.00%) high mild
2 (2.00%) high severe
vec time: [25.602 ns 27.162 ns 29.211 ns]
Found 7 outliers among 100 measurements (7.00%)
6 (6.00%) high mild
1 (1.00%) high severe
因此,使用数组而不是切片的版本与
try_into()
(性能相同),那么
复制from_slice()
,然后是其他人。
另一种选择是帮助LLVM消除绑定检查。它在这里有问题,因为你按递增顺序索引:我们检查索引0,但这对索引1没有任何意义,所以我们也检查它,然后是2,然后是3……但如果我们先检查最后一个索引,LLVM知道以前的所有索引也存在:
#[inline]
pub fn slot_key_array(address: &[u8], slot: &[u8]) -> [u8; 53] {
address[19];
slot[31];
// ...
}
这使此代码与数组版本不相上下:
array time: [4.6107 ns 4.6164 ns 4.6238 ns]
change: [-65.325% -64.755% -64.411%] (p = 0.00 < 0.05)
Performance has improved.