以 `byteorder` 读取 register-sized 数据是否有效?
Is reading register-sized data in `byteorder` efficient?
标题中的箱子是byteorder
。
以下是我们如何从 std::io::BufReader
. BufReader
implements the std::io::Read
trait. There is an implementation of byteorder::ReadBytesExt
中读取任何实现 Read
的类型的二进制数据。 ReadBytesExt
包含 read_u16
和其他读取二进制数据的方法。此实现:
fn read_u16<T: ByteOrder>(&mut self) -> Result<u16> {
let mut buf = [0; 2];
self.read_exact(&mut buf)?;
Ok(T::read_u16(&buf))
}
它将对buf
的引用传递给BufReader
;我想它在堆栈中传递了 buf
的地址。因此,结果 u16
从 BufReader
(内存)的内部缓冲区传输到上面的 buf
(内存),可能使用 memcpy
或其他东西。如果BufReader
通过直接从其内部缓冲区读取数据来实现ReadBytesExt
,效率会不会更高?还是编译器把buf
优化掉了?
TL;DR:全看优化大神了,不过要有效率。
这里的关键优化是内联,和往常一样,概率在我们这边,但谁知道...
只要对 read_exact
的调用是内联的,它就应该可以正常工作。
首先,它可以内联。在 Rust 中,“内部”调用总是静态分派——没有继承——因此 self.read_exact
中的接收器类型 (self
) 在编译时是已知的。因此,被调用的确切 read_exact
函数在编译时是已知的。
当然,是否无法确定它是否会被内联。实施时间相当短,所以机会很大,但这不在我们手中。
其次,如果内联会怎样?魔法!
可以看到实现here:
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
if self.buffer().len() >= buf.len() {
buf.copy_from_slice(&self.buffer()[..buf.len()]);
self.consume(buf.len());
return Ok(());
}
crate::io::default_read_exact(self, buf)
}
内联后,我们有:
fn read_u16<T: ByteOrder>(&mut self) -> Result<u16> {
let mut buf = [0; 2];
// self.read_exact(&mut buf)?;
if self.buffer().len() >= buf.len() {
buf.copy_from_slice(&self.buffer()[..buf.len()]);
self.consume(buf.len());
Ok(())
} else {
crate::io::default_read_exact(self, buf)
}?;
Ok(T::read_u16(&buf))
}
不用说,所有那些 buf.len()
调用都应该替换为 2
。
fn read_u16<T: ByteOrder>(&mut self) -> Result<u16> {
let mut buf = [0; 2];
// self.read_exact(&mut buf)?;
if self.buffer().len() >= 2 {
buf.copy_from_slice(&self.buffer()[..2]);
self.consume(2);
Ok(())
} else {
crate::io::default_read_exact(self, buf)
}?;
Ok(T::read_u16(&buf))
}
所以我们剩下 copy_from_slice
,一个 memcpy
调用 constant size (2).
诀窍在于 memcpy
非常特殊,它是大多数编译器的内置函数,当然在 LLVM 中也是如此。它是一个专门的内置函数,因此在特殊情况下——例如指定的常量大小恰好是寄存器大小——它的代码生成器可以专门用于……在 [= 的情况下的 mov
指令70=].
因此,只要 read_exact
是内联的,那么 buf
应该从头到尾都存在于一个寄存器中……在最理想的情况下。
在冷路径中,当调用default_read_exact
时,编译器将需要使用堆栈并传递一个切片。没关系。它不应该经常发生。
如果您发现自己反复执行 u16
读取序列,但是......您可能会发现读取更大的数组会更好,以避免重复 if self.buffer().len() >= 2
检查.
标题中的箱子是byteorder
。
以下是我们如何从 std::io::BufReader
. BufReader
implements the std::io::Read
trait. There is an implementation of byteorder::ReadBytesExt
中读取任何实现 Read
的类型的二进制数据。 ReadBytesExt
包含 read_u16
和其他读取二进制数据的方法。此实现:
fn read_u16<T: ByteOrder>(&mut self) -> Result<u16> {
let mut buf = [0; 2];
self.read_exact(&mut buf)?;
Ok(T::read_u16(&buf))
}
它将对buf
的引用传递给BufReader
;我想它在堆栈中传递了 buf
的地址。因此,结果 u16
从 BufReader
(内存)的内部缓冲区传输到上面的 buf
(内存),可能使用 memcpy
或其他东西。如果BufReader
通过直接从其内部缓冲区读取数据来实现ReadBytesExt
,效率会不会更高?还是编译器把buf
优化掉了?
TL;DR:全看优化大神了,不过要有效率。
这里的关键优化是内联,和往常一样,概率在我们这边,但谁知道...
只要对 read_exact
的调用是内联的,它就应该可以正常工作。
首先,它可以内联。在 Rust 中,“内部”调用总是静态分派——没有继承——因此 self.read_exact
中的接收器类型 (self
) 在编译时是已知的。因此,被调用的确切 read_exact
函数在编译时是已知的。
当然,是否无法确定它是否会被内联。实施时间相当短,所以机会很大,但这不在我们手中。
其次,如果内联会怎样?魔法!
可以看到实现here:
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
if self.buffer().len() >= buf.len() {
buf.copy_from_slice(&self.buffer()[..buf.len()]);
self.consume(buf.len());
return Ok(());
}
crate::io::default_read_exact(self, buf)
}
内联后,我们有:
fn read_u16<T: ByteOrder>(&mut self) -> Result<u16> {
let mut buf = [0; 2];
// self.read_exact(&mut buf)?;
if self.buffer().len() >= buf.len() {
buf.copy_from_slice(&self.buffer()[..buf.len()]);
self.consume(buf.len());
Ok(())
} else {
crate::io::default_read_exact(self, buf)
}?;
Ok(T::read_u16(&buf))
}
不用说,所有那些 buf.len()
调用都应该替换为 2
。
fn read_u16<T: ByteOrder>(&mut self) -> Result<u16> {
let mut buf = [0; 2];
// self.read_exact(&mut buf)?;
if self.buffer().len() >= 2 {
buf.copy_from_slice(&self.buffer()[..2]);
self.consume(2);
Ok(())
} else {
crate::io::default_read_exact(self, buf)
}?;
Ok(T::read_u16(&buf))
}
所以我们剩下 copy_from_slice
,一个 memcpy
调用 constant size (2).
诀窍在于 memcpy
非常特殊,它是大多数编译器的内置函数,当然在 LLVM 中也是如此。它是一个专门的内置函数,因此在特殊情况下——例如指定的常量大小恰好是寄存器大小——它的代码生成器可以专门用于……在 [= 的情况下的 mov
指令70=].
因此,只要 read_exact
是内联的,那么 buf
应该从头到尾都存在于一个寄存器中……在最理想的情况下。
在冷路径中,当调用default_read_exact
时,编译器将需要使用堆栈并传递一个切片。没关系。它不应该经常发生。
如果您发现自己反复执行 u16
读取序列,但是......您可能会发现读取更大的数组会更好,以避免重复 if self.buffer().len() >= 2
检查.