5 ms·
FYI, on my machine a naive Rust implementation (modulo plus println!) runs at the same speed as a naive Python implemention (16-17 MB/s), even compiled with --r
by yodelshady 5y ago
FYI, on my machine a naive Rust implementation (modulo plus println!) runs at the same speed as a naive Python implemention (16-17 MB/s), even compiled with --release. Naive C manages 160-170 MB/s. (And I fully admit the naive implementations are pretty much as far as I'm going with this).
I would guess it's the println! macro eating the time.
- pcwalton 5y agoIt's probably stdout locking. Call Stdout::lock.
- yodelshady 5y agoedit: weirder, naive Julia (1.7 on Linux) is terrible. 45 KB/s. Ruby is slightly slower than Python or Rust, 11 MB/s. I was expecting Python to be slowest among the interpreted languages, apparently not. Pypy yields a speedup to 70 MB/s. Summing naive implementations (for each i: start with empty string, append fizz/buzz based on the modulus of each, then conditionally append i and print): Julia 45 KB/s Ruby 11 MB/s CPython 15 MB/s Rust 16 MB/s Pypy 70 MB/s C 160 MB/s
- sc11 5y agoDo you mind sharing the Julia code? Seems odd that it's that far away from even Python.
- adgjlsfhk1 5y agoby default Julia uses locking for writes to guarantee thread safety when multiple writes are happening. That said, it's totally possible to go way faster using buffered IO.
- yodelshady 5y ago``` for i = 1:typemax(UInt64) s = "" if i % 3 == 0 s = s * "fizz" end if i % 5 == 0 s = s * "buzz" end if s == "" s = string(i) end println(s) end ``` Measured over 10 s after a 10 s burn-in period, Julia 1.7 on Ubuntu 21.10.
- Qem 5y agoI think recent versions of Ruby include a optional JIT compiler. Did you try it?
- beltsazar 5y agoThere are two reasons: 1) The println! macro will lock and unlock the stdout every time it's called. 2) Rust's stdout is line-buffered by default. In my machine the naive C implementation achieves 81.7 MiB/s, whereas a naive Rust implementation with println achieves 7.58 MiB/s. When I acquired the stdout's lock once and use writeln!(&mut stdout, ...) instead of println, I got 8.15 MiB/s. When I wrapped stdout with BufWriter, I got 299 MiB/s. use std::io::{BufWriter, Write}; fn main() { let stdout = std::io::stdout(); let stdout = stdout.lock(); let mut stdout = BufWriter::with_capacity(32 * 1024, stdout); for i in 1..1_000_000_000 { let _ = match (i % 3 == 0, i % 5 == 0) { (true, true) => writeln!(&mut stdout, "FizzBuzz"), (true, false) => writeln!(&mut stdout, "Fizz"), (false, true) => writeln!(&mut stdout, "Buzz"), (false, false) => writeln!(&mut stdout, "{}", i), }; } }
- yodelshady 5y agoWell TIL that printf won't lock stdout. Calling stdout.lock() by itself makes little difference for me. Using BufWriter as you have I get ~80 MiB/s (slightly ahead of PyPy). My implementation also has/had a String allocation in the loop, kicking that out got me to 120 MiB/s. I tend to write it as though the numbers/strings could be changed by a product manager at any time. Final edit/errata: I get up to 140 MiB/s by appending .to_string() in the integer case instead of format!(), which makes some sense. I didn't bother saving my original C implementation, but in retrospect I think I didn't use string concatenation, as I have with other languages. Redoing it with string-concatenation, C is slightly slower than Rust, 110 MiBs. Also makes sense as concatenation in C is slow.