Porting a Golang and Rust CLI tool to D
pingfrommorocco.blogspot.com
pingfrommorocco.blogspot.com
I want to note that you can call actual C functions from D without much extra effort.
Here’s a program that calls getpass: https://glot.io/snippets/fqf04b6wg2
I forgot to edit my comment to add that, for some reason, it doesn’t seem work on macOS. But if that’s not a problem for you, then I’m happy I could help.
Edit: I just read the code, and it seems like you found out about that before I could warn you. :)
When it comes to generated code, I can imagine the panic unwinding code being a noticeable contributor to the size of a Rust binary, but since D has exceptions I imagine the D binaries have comparable code for unwinding.
One feature D stole from Ada that is an absolutely inexcusable ommision in a modern programming is contract programming. Every time I start a new python script I end up writing helpers for function pre and postconditions because otherwise I can't rely on anything in the language's syntax.
Invariant clauses in D (e.g. this integer in my struct must be less than 5 when the struct is in a valid state) are an absolute godsend although they do not aid compiler optimisations despite some people saying otherwise (it could have changed but last time I checked the invariant does not make it's way to LLVM or GCC)
Having separate syntax is also very useful because it's much easier to see through my usual spaghetti to find what the code is actually supposed to do in the abstract (i.e. if the string should be a valid email, check it in the in {} clause rather than in the middle of the function)
Incidentally, Walter Bright considers contracts to be a low value feature of D that he considered removing. [0][1]
See https://github.com/johnthagen/min-sized-rust for aiming for small binary size.
This article shows a 93% size reduction, 2.7m -> 200kb, with some relatively simple modifications https://www.collabora.com/news-and-blog/blog/2020/04/28/redu....
Here is someone using Rust for a 3kb demoscene program https://www.codeslow.com/2019/12/tiny-windows-executable-in-....
Rust compiles seem to blow up pretty easily, e.g. I was cleaning out my SSD the other day an I noticed that a rust GUI framework (Iced?) was sat on about 2Gb of dependencies despite being significantly incomplete (but very promising)
That depends on your distro. The packages from dlang.org all do the static link config out-of-the-box, but some distro packages dynamic link by default instead.
Given the size of that file, it looks static linked. It dynamic links libc, pthread, and a couple other OS-level things, but all D libs including druntime, phobos, etc, are statically linked.
Might just be difference in dependencies.
And yes, a lot of it is unsafe, because it's calling into all of that stuff directly, and doing pointer arithmetic, etc. Check the post, it's good, the Conclusion section talks about this specifically.
This is really puzzling to me: in what context are you working to have hesitations between Rust and Zig? One is a production language, the other is an exciting experimental language mostly written by a single genius. Go for Zig if you want to have fun, and Rust if you want to actually build something. It's like hesitating between Kotlin and Idris!
>Rust downloaded a lot of crates, but that's probably because the Rust version of the code has more features than mine has. For example, it fetched rpassword, a tool that hides password characters as you type them into a terminal, much like Python's getpass function. That's one of the many things I don't have in my code.
Go Memory usage: 13mb
Rust Memory usage: 9mb
D Memory usage: 7mbGo has Google's direct backing, which is more what I was pointing towards.
D developers are all experts in the art of crafting compilers and language design.
9 women can't make a baby in a month
Imagine what they can do in 19 years.
And Go, by the virtue of having less features, has to allocate quite more. Eg: Go doesn't have lazy ranges / streams. Dynamic dispatch using interfaces has to allocate memory on heap.
Also, I expect D standard library to be optimized for Non-GC use case at least a part of it, because that's one of its use cases.
Compiler's escape analysis matters too. I'm told Go's escape analysis is not very sophisticated.
Last, it may be case of missing features in D version as a comment mentioned.
I would not have predicted D would be better than Rust in any measure if those are the number of maintainers.
I'd guess that the larger team must be able to do more. "Faster alone, further together".
Also, quick tip for anyone writing scripts with a GC or malloc/free: turn the collections/free off and just allocate until you finish the script. The OS collects the memory and you save a few seconds.
> Rust downloaded a lot of crates, but that's probably because the Rust version of the code has more features than mine has. For example, it fetched rpassword, a tool that hides password characters as you type them into a terminal, much like Python's getpass function. That's one of the many things I don't have in my code.
My guess is that you have experience with platforms such as the JVM or CLR (dot NET) and expect garbage collected languages to use up a lot of memory.
The JVM and CLR use generational/copying GC. These types of GC sacrifice memory to get efficient collection -- the more memory they use, the more efficient the GC [1].
Both Go and D use non-copying GC which has a lower memory overhead.
I am not sure about D, but Go also uses escape analysis so that data structures are allocated on the stack instead of the heap when possible which can help to reduce garbage and hence memory usage.
[1] The principle is similar to the one described in this paper where the GC time is independent of the amount of garbage: https://scholar.google.com/scholar?cluster=48058747733581794...
D sometimes does this but for the most part it depends on how you write the code. strings, large arrays, and class objects are typically GC'd but struct instances and small arrays are often not.
The code in the blog post appears to mostly use small stack memory.
The only weird thing i see in the blog post is 30 second compile. Yikes, this should be more like 3 second from scratch, max - it must use some heavier-than-necessary dependencies.
Go doesn't even use a stack compatible with the system abi and has probably the worst FFI of any language I've ever used, it has a very, err, unique type system. I mean there's a shit ton of great stuff too (compile times, great test patterns and pretty good tooling, the http stack and string manipulation and green threading is fantastic), I don't have it out for go! Meanwhile rust has a completely different memory model and type system and concurrency model, has probably the best FFI of any language outside of maybe some lisps and cpython and probably D (never used it), and they've taken forever to role out production ready http client/server stacks (though I think this is finally smoothing out!) I just don't see why they're tied together except a) static compilation and b) they emerged at roughly the same time.
Rust and go are very, very different languages aimed at completely different niches.