Description
Proposal
Problem statement
Using unbuffered I/O is a common performance footgun which has users asking why their Rust program is so slow.
Motivating examples or use cases
Issue #445 opened this discussion, "Attempt to help users avoid unbuffered small reads":
Users regularly come to the /r/rust subreddit looking for help when their Rust code doesn't perform as well as they expect. The second most common culprit (after debug vs release build) is doing unbuffered small reads from a file.
I'm wondering if there's anything we could do to help users catch or avert this more easily.
Solution sketch
Add two File
constructors that wrap the result in buffers:
impl File {
pub fn open_buffered<P: AsRef<Path>>(path: P) -> io::Result<io::BufReader<File>> {
File::open(path).map(io::BufReader::new)
}
pub fn create_buffered<P: AsRef<Path>>(path: P) -> io::Result<io::BufWriter<File>> {
File::create(path).map(io::BufWriter::new)
}
}
These are trivial, but the main advantage is that such explicit methods will appear in auto-complete lists. Hopefully that will raise more programmer awareness that the basic open
and create
methods are not buffered, by omission, so they may consider if that's something they want.
Users with deeper OpenOptions
will still need to decide which kind of buffering makes sense in their case. Similarly, if we want create_new_buffered
, we would probably choose BufWriter
, whereas create_new
is opened in read-write mode.
Alternatives
-
The status quo is that
File
documents this:File
does not buffer reads and writes. For efficiency, consider wrapping the file in aBufReader
orBufWriter
when performing many small read or write calls, unless unbuffered reads and writes are required.We could deem that sufficient.
-
Attempt to help users avoid unbuffered small reads #445 has several other ideas like debug assertions, new lints, or cargo tooling for performance footguns. Any of those could still be implemented in addition to this proposal.
What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
- We think this problem seems worth solving, and the standard library might be the right place to solve it.
- We think that this probably doesn't belong in the standard library.
Second, if there's a concrete solution:
- We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
- We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.