5 ms·
Unbuffered I/O is slower than buffered I/O regardless of language. It’s not just a Rust phenomenon. Making syscalls like write() every time you want to send ev
by otterley 5y ago
Unbuffered I/O is slower than buffered I/O regardless of language. It’s not just a Rust phenomenon.
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.
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.
- yongjik 5y ago> 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 in the camp that logging should be always unbuffered. If you buffer logs, and your process dies for any reason, you lose your log exactly when you really need it to know what happened.
- otterley 5y agoBuffered I/O is usually not lost when a process exits. Usually buffers are flushed automatically upon exit when the file descriptors are closed. And if a program truly crashes and leaves a core behind, the unflushed buffers should be found in the core file.
- yongjik 5y agoIf the process exits in an orderly fashion then, yes, it will flush logs, and also do a bunch of other nice things. The problem arises when it dies in a not-so-nice fashion. (Also, not every environment is running with unlimited core size.)
- silisili 5y ago> In general, the only time anyone should use unbuffered output is when there’s a real person consuming it I think this might be a little -too- general, as it's pretty workload dependent. I mean, if you just want to write a blob of bytes once, just write the blob IMO. I don't think using a buffer in those cases ever really hurts, but sometimes makes what should be simple a bit more complicated than necessary.
- molyss 5y agoEven for stuff as simple as handling LOBs, I buffer on both read and writes. Some DB APIs really do “what you tell them to”, even if that means being unbearably slow when unbuffered The worst I’ve seen was actually streaming the LOB on getLength. Since there’s no JDBC API for lob.getContentAsArray(), you need to call lob.getContent(1, lob.length()) (notice that you’d start at index 1…), which by default would stream the LOB twice under the scene!
- harles 5y agoDefinitely too general of a statement. I previously worked on real-time mapping and the only way to get the necessary write performance was to go unbuffered and manually split off a block at a time to write. The same was true of reading - it was faster and made sure the # of syscalls in any frame was predictable. As a side note, the real issue here is that unbuffered is the default. Other languages and libraries I’m aware of make it difficult to get in such a mode.
- brundolf 5y agoLots of people are writing Rust code who aren't previously versed in the nuances of optimizing bare-metal code. They're (reasonably) expecting amazing performance, because Rust tends to give you that by default compared with higher-level languages. But repeated calls to println!() is one of the biggest and easiest-to-make performance mistakes for those newcomers, so I think it's fair to call it out. With that said, the OP could definitely have done a better job making it clear that this problem isn't specific to Rust
- not2b 5y agoDevelopers from other languages, like C, C++, or even Python, would not have expected this particular problem, because it wouldn't have occurred to them that the thing they get from doing a file-open call and that the JSON reader library accepts is a raw file descriptor (for C programmers, the result of 'open' rather than 'fopen') that does unbuffered I/O. In other languages libraries are oriented around buffered I/O structures (a FILE or an iostream).
- BoorishBears 5y agoTo me the biggest reason for the surprise is the signature for 'write' I'm used to a raw stream allowing a few specific operations: - read/write/peek one byte - write a byte array, but you must explicitly specify length and offset So seeing a call that simply takes an entire byte string and writes it gives me "buffered I/O vibes". It's hard to quantify, but with how ubiquitous that pattern is I'd be surprised if there are a lot of people who expect similar.
- not2b 5y agoThis had been upvoted and now it is downvoted, even though it is obviously true, as the article demonstrates.
- oconnor663 5y agoFor what it's worth, I run into the same nuances frequently in Python. For example I often want to run some_script.py | tee log.txt But by default, Python will turn off line buffering when it detects it's not talking to a tty. So unless you know to call flush, or to add the right env var or interpreter flag, you're not going to see any early output from tee. This can be super confusing for newer programmers.
- loeg 5y agoTotally agree. In contrast, for example, FILE objects in C are (mostly) buffered by default. So in that sense, Rust defaults are less sensible for some developers who would be better served by buffering, but aren't aware that they need it. But I also think that Rust's default is totally reasonable and it's just something you have to learn about as a systems programmer.