Unbuffered I/O is slower than buffered I/O regardless of language. It’s not just a Rust phenomenon.<p>Making syscalls like write() every time you want to send even a character of output down a stream is much more latent than just sticking bytes in a buffer.<p>In general, the only time anyone should use unbuffered output is when there’s a real person consuming it - for example, when you’re prompting a user for feedback or input.
I'm surprised serde_json takes a std::io::Read rather than a std::io::BufRead. I wonder why they made this choice.<p>Not only does using plain Read make this mistake possible, but it also means they have to be doing byte-by-byte processing. [1] So not really possible to do SIMD-oriented parsing acceleration like simdjson uses. (I gather simdjson's approach is quite sophisticated, but a simpler version is to scan for the next delimiter via a memchr2 on " and \, then have a routine that validates the characters until then are valid via SIMD methods.)<p>[1] or do larger reads into their own buffer. But they're clearly not doing that given this strace output. And it'd be redundant when folks do use BufRead.
I kind of understand why Rust made buffered IO explicit and on-demand instead of going down the C route and making it the default, but still I think there's a significant risk lots of programs are going to be suboptimal due to developer ignorance or the expectation of having buffered IO by default like with stdio and iostreams.<p>It would have been IMHO probably less clean but safer to make the standard IO constructs like File buffered by default (which is what users expect in the vast majority of cases) and provide on the side unbuffered variants. Thankfully modern OSes with caches and stuff somewhat mitigate this thing, but it can make IO on less advanced systems way more problematic than it should be.
Alternative title: Poor Interface Design Makes Most Rust Programs Much Slower<p>That programmers need to consider this stuff on each new project is a failure of the standard library design, it's a problem not even C++ suffers from
>"In this post, we will look at a common source of bad performance on Rust code that can trip up even veteran developers, and what you can do when this happens in your programs."<p>"Veteran developers", the sort of who would use Rust after having worked in C/C++/etc would know very well that buffering on very granular IO is a must except some specific situations. Otherwise they're anything but. And it has nothing to do with Rust. This is of course when IO libraries used do not already take care of that.
I started a Rust project several months ago and quickly became frustrated with the state of basic terminal libraries. None of them seemed to implement the buffering and minimal-refresh of ncurses, so I was forced to rely on one of the many shifting ncurses binding libraries and all the unsafeness that comes with that (or reimplement my own—way out of scope for the little toy I was building). What's the state of the art for raw terminal libraries (not TUI helpers) in Rust nowadays?
I am not all that knowledgeable about rust but I think that if you really want to decrease the numbers of system calls when interacting with files, writev and readv should be considered. I wonder if BuffWriter/Reader could be made to use it, as it would have the same "atomic" behaviour and would forgo the need to concat strings. Still, the char*s could become invalidated once the system call is made.
Multiple of your “senior” developers didn’t catch this in a “senior” developer’s PR? Have they ever read any of the IO documentation, since this is pointed out literally everywhere in the docs?<p><a href="https://doc.rust-lang.org/std/fs/struct.File.html" rel="nofollow">https://doc.rust-lang.org/std/fs/struct.File.html</a><p><a href="https://doc.rust-lang.org/std/io/trait.Read.html" rel="nofollow">https://doc.rust-lang.org/std/io/trait.Read.html</a>