我需要将Rust中字符串的前8个字节转换为一个u64,大端字节。此代码几乎有效:
fn main() {
let s = String::from("01234567");
let mut buf = [0u8; 8];
buf.copy_from_slice(s.as_bytes());
let num = u64::from_be_bytes(buf);
println!("{:X}", num);
}此代码存在多个问题。首先,只有当字符串正好有8个字节长时,它才能工作。.copy_from_slice()要求源和目标都具有相同的长度。如果字符串太长,这很容易处理,因为我可以只获取一个适当长度的片段,但是如果字符串是短的,它就不能工作。
另一个问题是,这段代码是非常性能敏感的函数的一部分。它在一个大型数据集上运行在一个紧密的循环中。
在C中,我只需要对buf、memcpy对正确的字节数进行零化,然后对一个未签名的long进行强制转换。
有什么方法可以做到这一点在铁锈,因为它运行一样快?
发布于 2022-03-20 02:27:45
您只需修改现有代码,以便在复制时考虑到长度:
let len = 8.min(s.len());
buf[..len].copy_from_slice(&s.as_bytes()[..len]);当然,如果字符串很短,这将把字节复制到u64中最重要的部分中。
至于性能:在这个简单的测试main()中,转换被完全优化成一个常量整数。因此,我们需要一个显式函数或循环:
pub fn convert(s: &str) -> u64 {
let mut buf = [0u8; 8];
let len = 8.min(s.len());
buf[..len].copy_from_slice(&s.as_bytes()[..len]);
u64::from_be_bytes(buf)
}这个(在锈菌游乐场上)生成程序集:
playground::convert:
pushq %rax
movq %rdi, %rax
movq $0, (%rsp)
cmpq $8, %rsi
movl $8, %edx
cmovbq %rsi, %rdx
movq %rsp, %rdi
movq %rax, %rsi
callq *memcpy@GOTPCREL(%rip)
movq (%rsp), %rax
bswapq %rax
popq %rcx
retq与仅仅发出指令复制字节相比,我有点怀疑memcpy调用实际上是一个好主意,但我不是指令级性能方面的专家,而且可能至少会与C代码一样显式地调用memcpy()。我们所看到的是,在编译的代码中没有分支,只有一个有条件的举动,大概是为了处理8与len()的选择--以及无边界检查恐慌。
(当这个函数或代码片段内联到一个更大的循环中时,生成的程序集当然会有所不同--希望这样做会更好。)
https://stackoverflow.com/questions/71542843
复制相似问题