6 ms·
If you are looking for a non-joke implementation[0]. Excluding the test code at the bottom, the Rust version is a bit less than 120 lines. [0] https://github.c
by 3eb7988a1663 1y ago
If you are looking for a non-joke implementation[0]. Excluding the test code at the bottom, the Rust version is a bit less than 120 lines.
[0] https://github.com/uutils/coreutils/blob/main/src/uu/yes/src/yes.rs https://github.com/uutils/coreutils/blob/main/src/uu/yes/src...
- deleted 1y ago[deleted]
- unscaled 1y agoGNU core utils is 134 lines of code, not 50, so the Rust version is even slightly shorter. You can make yes a lot shorter in both C and Rust, but this size goes into speed. For reference, OpenBSD's yes is just 17 lines of code[2]. It essentially boils down to this: int main(int argc, char *argv[]) { if (pledge("stdio", NULL) == -1) err(1, "pledge"); if (argc > 1) for (;;) puts(argv[1]); else for (;;) puts("y"); } This is as simple as it gets, but the joke yes-rs implementation is right about one thing: "blazing fast" speed often comes at the cost of greatly increased complexity. The BSD implementation of yes is almost 10 times shorter than the GNU implementation, but the GNU implementation is 100 times faster[3]. [1] https://github.com/coreutils/coreutils/blob/master/src/yes.c https://github.com/coreutils/coreutils/blob/master/src/yes.c [2] https://github.com/openbsd/src/blob/master/usr.bin/yes/yes.c https://github.com/openbsd/src/blob/master/usr.bin/yes/yes.c [3] https://www.reddit.com/r/unix/comments/6gxduc/how_is_gnu_yes_so_fast/ https://www.reddit.com/r/unix/comments/6gxduc/how_is_gnu_yes...
- 3eb7988a1663 1y agoThat reddit thread has some amazing benchmarks. The GNU-yes $ yes | pv > /dev/null ... [10.2GiB/s] ... The way I (not a C programmer) would have written it void main() { while(write(1, "y\n", 2)); // 1 is stdout } $ gcc yes.c -o yes $ ./yes | pv > /dev/null ... [6.21 MiB/s] ...
- phh 1y agoWhich implies you get pretty much 3M syscall per second. Which is a good order magnitude to know
- nasretdinov 1y agoI don't believe puts is performing unbuffered I/O though. It's a libc function, not a direct syscall. Correct me if I'm wrong of course
- lieks 1y agoThe write(2) libc function is just a C wrapper for the syscall. It's the functions from stdio.h that are buffered.
- nasretdinov 1y agoAh ok sorry I got confused by the comments nesting level. I thought we were talking about the OpenBSD's version which uses puts
- pona-a 1y agoAs a non-system-programmer, here's my attempt in Odin. yes | pv > /dev/null 0:00:15 [1.12GiB/s] build/yes | pv > /dev/null 0:00:20 [1.03GiB/s] package main import "core:sys/linux" import "core:os" import "core:strings" main :: proc() { msg := "y" if len(os.args) == 1 else os.args[1] msg = strings.concatenate({msg, "\n"}) buf := transmute([]u8) strings.repeat(msg, 8192) for { linux.write(linux.STDOUT_FILENO, buf) } }
- forgotpwd16 1y agoReplace `write(..)` with `puts("y")` and you'll be an order of magnitude faster. This is due to `puts` (`printf` too) being buffered (data isn't written to term/file immediately but retained in memory until some point). Improving this process (as seen in the reddit thread) gets GNU-yes.
- nasretdinov 1y agoIn this case OpenBSD version does a much better job imo (although I don't agree with the lack of braces). The performance of such a tool does not matter at all, and a larger implementation is not only unnecessary, but it can actually introduce bugs in otherwise completely straightforward code
- mrweasel 1y agoThe OpenBSD version of true is also amazing: https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/usr.bin/true/true.c?rev=1.1&content-type=text/plain https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/usr... The GNU version of true/false is more interesting. All the logic is in true and false just redefined the EXIT_STATUS and imports all of true.c. https://github.com/coreutils/coreutils/blob/master/src/false.c https://github.com/coreutils/coreutils/blob/master/src/false...
- johnisgood 1y agoFor reference, here is true.c: https://github.com/coreutils/coreutils/blob/master/src/true.c https://github.com/coreutils/coreutils/blob/master/src/true.....
- AlecSchueler 1y agoSo basically they also introduced the complexity of respecting --help and --version.
- johnisgood 1y agoThey did, because it is written somewhere that all GNU programs must conform to having --help and --version. I forgot where I read it.
- 1718627440 1y agoThe GNU Coding Standard: https://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces https://www.gnu.org/prep/standards/standards.html#Command_00...
- layer8 1y ago> This is as simple as it gets It unnecessarily duplicates the for loop. I would have written something like: char *what = argc > 1 ? argv[1] : "y"; for (;;) puts(what);
- williamdclt 1y agoWhat a waste of 8 bytes! :)
- layer8 1y agoIt’s not about bytes, it’s about duplicating logic that should inherently be the same. If you change something about the loop or the puts, you now have to take care to change it identically in two places to be consistent. That’s a situation that should be avoided, and is what makes it not “as simple as it gets”.
- williamdclt 1y agoI was being humorous, but tbh it’s not so clear cut! In 99% of cases, yes of course you’re right, factor this loop. In this specific case? This is trivial code, that will likely _never_ change. If it does change, it’s extremely unlikely that the two loops would accidentally diverge (the dev would likely not miss one branch, tests would catch it, reviewers would catch it). So if you get any upside by keeping the two loops, it might be worth it. Here you get 8 bytes back. I honestly can’t see how that would ever matter, but hey it’s _something_, and of course this is a very old program that was running on memory-constrained machines. So it’s a trade-off of (minor) readability versus (minor) runtime optimisation. I think it’s the better choice (although it’s very minor). Or maybe there’s a better reason they chose this pattern… can’t imagine the compiler would generate worse code, but maybe it did back in the days?
- layer8 1y agoI agree that it’s borderline pedantic for this simple code, but I also find it an obvious code smell, contradicting the “as simple as it gets”. If you consistently deduplicate code that is supposed to do the same and evolve the same, then any duplicated code sticks out as a statement of “this isn’t the same”, and in the present case it then makes you wonder what is supposed to be different about both cases. In other words, such code casts doubt on one’s own understanding, raising the question whether one might be overlooking an important conceptual reason for why the code is being kept duplicated. So in that sense I disagree that the duplicated version is more readable, because it immediately raises unanswered questions. About possible performance reasons, those need an explanatory comment, exactly for the above reason. And also, if performance reasons warrant complicating the code, then it isn’t “as simple as it gets” any more. I was commenting because I disagreed with that latter characterization.
- johnisgood 1y agoWhat do you mean non-joke? How can you tell? How is this a joke, and how is that not a joke?! What makes the distinction?
- ku1ik 1y agoHave you looked at the source code? It’s obvious.
- johnisgood 1y agoYeah, and what makes uutils not a joke? It is not immediately obvious to me.
- ramon156 1y agoJust checked the readme. If you can't see the obvious tongue-in-cheek way of writing then I don't know what to tell you.
- johnisgood 1y agoI understand that it is intended as a joke, but jokes often reveal underlying truths. This particular one highlights very real issues, and humor helps us see it through a clearer lens. That said, how can we be certain that uutils is not a joke? Is it purely the intent behind it that distinguishes it? This joke project has a lot of truths in it that others do dead seriously; something to think about.