Description
I feel like a pretty common pitfall for beginning Rust programmers is to try writing a program that uses println!
to print a lot of lines, compare its performance to a similar program written in Python, and be (rightly) baffled at the fact that Python is substantially faster. This occurred most recently here: https://www.reddit.com/r/rust/comments/bl7j7j/hey_rustaceans_got_an_easy_question_ask_here/emx3bhm/
The reason why this happens is because io::Stdout
unconditionally uses line buffering, regardless of whether it's being used interactively (e.g., printing to a console or a tty) or whether it's printing to a file. So if you print a lot of lines, you end up calling the write
syscall for every line, which is quite expensive. In contrast, Python uses line buffering when printing interactively, and standard block bufferring otherwise. You can see more details on this here and here.
In my opinion, Rust should adopt the same policy as Python. Indeed, there is even a FIXME item for this in the code:
Lines 401 to 404 in ef01f29
I think this would potentially solve a fairly large stumbling block that folks run into. The CLI working group even calls it out as a performance footgun. And also here too. Additionally, ripgrep rolls its own handling for this.
I can't think of too many appreciable downsides to doing this. It is a change in behavior. For example, if you wrote a Rust program today that printed to io::Stdout
, and the user redirected the output to a file, then the user could (for example) tail
that output and see it updated as each line was printed. If we made io::Stdout
use block buffering when printing to a file like this, then that behavior would change. (This is the reasoning for flags like --line-buffered
on grep
.)
cc @rust-lang/libs