Furthermore, if you dig deeper, you'll find that D offers far greater control over its garbage collector than any other high-level language, to the point that you can eagerly free chunks of allocated memory, minimizing or eliminating garbage collector stops where it matters.
> FFmpeg is proudly written in the C programming language for the highest performance. Other fashionable languages like C++, C#, Rust, Go etc do not meet the needs of FFmpeg.
How does D perform in benchmarks against other programming languages?
[0] https://www.linkedin.com/posts/ffmpeg_ffmpeg-is-proudly-writ...
But D is not included in the benchmarks game at debian.net, which isn't fair to D either as far as I can tell.
https://dlang.org/spec/interfaceToC.html
Dunno about the Debian benchmarks game or their build environment. I did my own benchmarks and it was quite easy to write working D code compared to C, C++ or Rust. I used LDC, the LLVM D compliler as opposed to DMD. Dub is not that seamless compared to Cargo but given that you have to set things up manually, it doesn't encourage dependency hell.
If you're writing networking code, Go is probably a better choice than vibe.d.
Weka have a realtime distributed filesystem written in D, used for ML/HPC workloads.
This https://github.com/weka ?
Most of the D repositories appear to have very little activity. The Go repositories seem to have more activity.
An example of Weka talking about how they use D (there are more examples at this and other dconf presentations): https://www.youtube.com/live/Ou4KUBjr_78?si=bPOs-19gPemQe_ap...
Do you happen to know why D has not been accepted into the benchmarks games at debian.net? I heard that D developers contributed D code, but that D was never accepted.
C# C interop is pretty smooth, Java is a different story. The fact that C# is becoming the GC language in game dev is proving my point.
>Furthermore, if you dig deeper, you'll find that D offers far greater control over its garbage collector than any other high-level language, to the point that you can eagerly free chunks of allocated memory, minimizing or eliminating garbage collector stops where it matters.
Yes, and the no-gc stuff was just attempts to backpedal on the wrong initial decision to fit into the use-cases they should have targeted from the start in my opinion.
Look D was an OK language but it had no corporate backing and there was no case where it was "the only good solution". If it was an actual C++ modernization attempt that stayed C compatible it would have seen much better adoption.
True, but you still need to either generate or manually write the bindings. In D, you just import the C headers directly without depending on the bindings' maintainers.
> If it was an actual C++ modernization attempt that stayed C compatible it would have seen much better
Any D compiler is literally also a C compiler. I sincerely don't know how can one be more C compatible than that.
> Yes, and the no-gc stuff was just attempts to backpedal on the wrong initial decision
I think that it was more of an attempt to appease folks who won't use GC even with a gun to their head.
Meanwhile look at how popular Zig is getting 2 decades later. Why is that not D ? D also has comp-time and had it for over a decade I think ? Zig proves there's a need that D was in the perfect spot to fill if it did not make the GC decision - and we could have had 2 decades of software written in D instead of C++ :)
This is perfectly fair.
> D was in the perfect spot to fill if it did not make the GC decision
I just find it hard to believe that the GC is the one big wart that pushed everyone away from the language. To me, the GC combined with the full power of a systems language are the killer features that made me stick to D. The language is not perfect and has bad parts too, but I really don't see the GC as one of them.
regarding kitchen-sink-ness it's at least nowhere near as bad as C++, but that bar is basically below the ground anyway so it's not much to write home about.
You never get a second chance at making a good first impression.
I believe that many people that gladly use Rust or Zig or Go nowadays would be quite happy with D if they were willing to give it a fair evaluation. But I still often find people going "D? I would never use a language where the ecosystem is split between different standard libraries"/"D? No thanks, I prefer compilers that are open source" or similar outdated claims. These things have not been true for a long time, but once they are stuck in the heads of the people, it is over. And these claims spread to other people and get stuck there.
If you do not want to use a GC, it is trivial to avoid it and still be able to use a large chunk of the ecosystem. But often avoiding GC at all costs is not even necessary - you mostly want to avoid it in specific spots. Even many games today are written with tasteful usage of GC.
The one thing that really is a fair disadvantage for D is its small community. And the community is small because the community is too small (chicken/egg) and many believe in claims that have not been true for a long time ...
There's a good number of younger programmers like myself who've never heard of D, say, before 2017 when those false claims were still true. Our first impression of D comes from its state today, which is not that far behind from other emerging languages.
Popularity is not proof of anything. C# is popular because it’s made by Microsoft and rode the OOP hype.
Respectfully, it doesn't prove your point. Unity is a commercial product that employed C# because they could sell it easily, not because it's the best language for game dev.
Godot supports C# because Microsoft sponsored the maintainers precisely on that condition.
That is just the Unity effect. Godot adopted C# because they get paid to do so by Microsoft.
C# allows for far lees control over the garbage collection compared to D. The decision to use C# is partly responsible for the bad reputation of Unity games as it causes a lot of stutters when people are not very careful about how to manage the memory.
The creator of the Mono runtime actually calls using C# his Multi-million dollar mistake and instead works on swift bindings for Godot: https://www.youtube.com/watch?v=tzt36EGKEZo
sidenote, I wonder how many other low-level to high-level compilers exist out there. can't be many.
Allegedly some games are now managing to ship on console with ports of .NET NativeAOT.
1. Runtime: A runtime is any code that is not a direct result of compiling the program's code (i.e. it is used across different programs) that is linked, either statically or dynamically, into the executable. I remember that when I learnt C in the eighties, the book said that C isn't just a language but a rich runtime. Rust also has a rich runtime. It's true that you can write Rust in a mode without a runtime, but then you can barely even use strings, and most Rust programs use the runtime. What's different about Java (in the way it's most commonly used) isn't that it has a runtime, but that it relies on a JIT compiler included in the runtime. A JIT has pros and cons, but they're not a general feature of "a runtime".
2. GC: A garbage collector is any mechanism that automatically reuses a heap object's memory after it becomes unreachable. The two classic GC designs, reference counting and tracing, date back to the sixties, and have evolved in different ways. E.g. in the eighties and nineties there were GC designs where the compiler could either infer a non-escaping object's lifetime and statically insert a `free` or have the language track lifetimes ("regions", 1994) and have the compiler statically insert a `free` based on information annotated in the language. On the other hand, in the eighties Andrew Appel famously showed that moving tracing collectors "can be faster than stack allocation". So different GCs employ different combination of static inference and dynamic information on object reachability to optimise for different things, such as footprint or throughput. There are tradeoffs between having a GC or not, and they also exist between Rust (GC) and Zig (no GC), e.g. around arenas, but most tradeoffs are among the different GC algorithms. Java, Go, and Rust use very different GCs with different tradeoffs.
So the problem with using the terms "runtime" and "GC" colloquially as they're used today is not so much that it differs from the literature, but that it misses what the actual tradeoffs are. We can talk about the pros and cons of linking a runtime statically or dynamically, we can talk about the pros and cons of AOT vs. JIT compilation, and we can talk about the pros and cost of a refcounting/"static" GC algorithm vs a moving tracing algorithm, but talking in general about having a GC/runtime or not, even if these things mean something specific in the colloquial usage, is not very useful because it doesn't express the most relevant properties.
Swift seems to require reference counting significantly more than Rust.
You see OP is trying to murk the waters when they claim C has a runtime. While there is a tiny amount of truth to that, in the sense that there’s some code you don’t write present at runtime, if that’s how you define runtime the term loses all meaning since even Assemblers insert code you don’t have to write yourself, like keeping track of offsets and so on. Languages like Java and D have a runtime that include lots of things you don’t call yourself, like GC obviously, but also many stdlib functions that are needed and you can’t remove because it may be used internally. That’s a huge difference from inserting some code like Rust and C do. To be fair, D does let you remove the runtime or even replace it. But it’s not easy by any means.
Except for the memory management literature, because it's interested in the actual tradeoffs of memory management. A compiler inferring lifetimes, either automatically for some objects or for most objects based on language annotations, has been part of GC research for decades now.
The distinction of working at compile time or runtime is far from huge. Working at compile time reduces the work associated with modifying the counters in a refcounting GC in many situations, but the bigger differences are between optimising for footprint or for throughput. When you mathematically model the amount of CPU spent on memory management and the heap size as functions of the allocation rate and live set size (residency), the big differences are not whether calling `free` is determined statically or not.
So you can call that GC (as is done in academic memory management research) or not (as is done in colloquial use), but that's not where the main distinction is. A refcounting algorithm, like that found in Rust's (and C++'s) runtime is such a classic GC that not calling it a GC is just confusing.
But is it not easy to opt out of in C, C++, Zig and Rust, by simply not using the types that use reference counting?
And how does your performance analysis consider techniques like arenas and allocating at startup only?
In C, Zig, and C++ sure. In Rust? Not without resorting to unsafe or to architectural changes.
> And how does your performance analysis consider techniques like arenas and allocating at startup only?
Allocating at startup only in itself doesn't say much because you may be allocating internally - or not. Arenas indeed make a big difference and share some performance behaviours with moving-tracing collectors, but they can practically only be used "as god intended" in Zig.
I should add that the JVM (and Go) also infers lifetime for non-escaping objects and "allocates" them in registers (which can spill to the stack; i.e. `new X()` in Java may or may not actually allocate anything in the heap). The point is that different GCs involve compiler-inferred lifetimes to varying degrees, and if there's a clear line between them is less the role of the compiler (although that's certainly an interesting detail) and more whether they generally optimise for footprint (immediate `free` when the object becomes unreachable) or throughput (compaction in a moving-tracing collector, with no notion of `free` at all).
There are also big differences between moving and non-moving tracing collectors (Go's concurrent mark & sweep and Java's now removed CMS collector). A CMS collector still has concepts that resemble malloc and free (such as free lists), but a moving one doesn't.
Look at C#'s competitive placings with C++/Rust on The Computer Benchmark game due to .NET's ruthless optimization.
And .NET is moving heavily into the AoT/pre-compilation direction for optimization reasons as well (source generators, AoT).
If you look at the change logs for the past few versions of the framework from perf perspective the most significant moves are : introduce new primitives to avoid allocating, move more logic to compile time, make AoT better and work with more frameworks.
A programming language having a GC doesn't mean every single allocation needs to be on the heap.
C# is finally at the sweet spot languages like Oberon, Modula-3 and Eiffel were on the late 90's, and unfortunely were overshadowed by Java's adoption.
Go and Swift (RC is a GC algorithm) are there as well.
D could be there as well on the mainstream, if there was a bit more steering into what they want to be, instead of having others catching up on its ideas.
That is what made me look into the language after getting Andrei Alexandrescu's book.
Note that even Rust's success, has triggered managed languages designers to research how far they can integrate linear, affine, effects, dependent types into their existing type systems, as how to combine the best of both worlds.
To the point that even Rust circles now there are those speaking about an higher level Rust that is supposed be more approachable.
Modern .NET isn't even difficult to avoid allocations, with the Span<T> API and the work they've done to minimize unnecessary copies/allocs within the std lib.
(I say this as a Kotlin/JVM dev who watches from the sideline, so not even the biggest .NET guy around here)
Yes, and that's where D is majorly superior to C# -- it's flexible enough for you to go down to the metal for the critical parts.
You are forgetting that a language with a less mature ecosystem isn't much help.
Yes, they added AOT but it's still challenging to do anything that requires calling into the OS, because you're going to need the bindings. It will still add some overhead under the hood and more overhead will you need to add yourself to convert the data to blittable types and back.
Mixing C# with other languages in the same project is also difficult because it only supports MSBuild.
> You are forgetting that a language with a less mature ecosystem isn't much help.
Fair.
Rust also has issues using anything besides cargo.
D also has its own build system but it's not the only option. Meson officially supports building D sources. You could also easily integrate D with SCons, though there's no official support.
You don't. Any D compiler is a C compiler too, so it can take C headers without bindings or any overhead added.
Hint, before keeping to discuss what D can and cannot do, better go look how long I have been around on D forums, or existing projects on my Github.
Anything that has stable ABI does.
> go look how long I have been around on D forums
You claimed higher up in this thread that D requires bindings to interoperate with C API. You don't seem that well informed really.
No, this is not true. You can invoke the compiler directly with no direct call to MSBuild what so ever.
Even using the dotnet command, which uses MSBuild under the hood, you are free to use your own build system. As an example - this code uses a Makefile to invoke the build: https://github.com/memsom/PSPDNA
If you want to call csc directly, it will compile with args just fine. And, if you have a working C# compiler on you platform, whether or not it uses MSBuild behind the scenes is kind of inconsequential.
You may also directly call the msbuild command, and it more or less does the same thing as the dotnet command, but hardly anyone eve calls msbuild directly these days.
Not to mention that writing a high quality GC is a monumental task - it took those decades for C# and Java to get decent - very few projects have the kind of backing to pull that off successfully.
In practical terms think about the complexity of enabling WASM that someone mentioned in this thread when you reuse C runtime and skip tracing GC.
I'm kind of venting in the thread to be fair, Walter Bright owes me nothing and it's his project, I had fun playing with it. I'm just sad we couldn't have gotten to Zig 20 years ago when we were that close :)
Do you want GC? Great! Do not want GC? Well, you can turn it off, and lose access to most things. Do you want a borrow-checker? Great, D does that as well, though less wholeheartedly than Rust. Do you want a safer C/memory safety? There's the SafeD mode. And probably more that I forget.
I wonder if all these different (often incompatible) ways of using D ends up fragmenting the D ecosystem, and in turn make it that much harder for it to gain critical mass
My (likely unfair) impression of D is that it feels a bit rudderless: It is trying to be too many things to too many people, and as a consequence it doesn't really stand out compared to the languages that commit to a paradigm.
Nim kind of does that, too.
FWIW, I think "standing out" due to paradigm commitment is mostly downstream of "xyz-purity => fewer ways to do things => have to think/work more within the constraints given". This then begs various other important questions, of course.. E.g., do said constraints actually buy users things of value overcoming their costs, and if so for what user subpopulations? Most adoption is just hype-driven, though. Not claiming you said otherwise, but I also don't think the kind of standing out you're talking about correlates so well to marketing. E.g., browsers marketed Javascript (which few praised for its PLang properties in early versions).
The more positive phrasing would be that it is a very pragmatic language. And I really like this.
Currently opinionated langues are really in vogue. Yes they are easier to market but I have personally very soured on this approach now that I am a bit older.
There is not one right way to program. It is fun to use on opinionated language until you hit a problem that it doesn't cover very well and suddenly you are in a world of pain. I like languages that give me escape hatches. That allow me to program they way I want to.
What "most things" are these?
There's errdefer, which only defers if there was an error, but presumably you meant what you wrote, and not that.
BTW, D was the first language to have defer, invented by Andrei Alexandrescu who urged Walter Bright to add it to D 2.0 ... in D it's spelled scope(exit) = defer, scope(failure) = errdefer, and scope(success) which is only run if no error.
As for "I think it would be move productive if you ...", that's just rude. I wasn't criticizing Fil-C, just pointing out an inaccuracy.
"That's just rude" - oh my, never though asking for evidence of a performance problem on HN would be "rude". And yes, if something is actually 5x slower, it's very likely a performance bug that can be fixed, as FillC's author has made it clear in several opportunities.
It's sad that people lie here. It's commonly recognized that programs run 1.5-5 times slower.
> if something is actually 5x slower, it's very likely a performance bug that can be fixed, as FillC's author has made it clear in several opportunities.
Another lie from someone who doesn't even know what the program is called.
1. Very load overhead calling of native libraries. Wrapping native libraries from Java using JNI requires quite a bit of complex code, configuring the build system, and the overhead of the calls. So, most projects only use libraries written in a JVM-language -- the integration is not nearly as widespread as seen in the Python world. The Foreign Function and Memory (FFM) API is supposed to make this a lot easier and faster. We'll see if projects start to integrate native libraries more frequently. My understanding is that foreign function calls in Go are also expensive.
2. Doesn't require a VM. Java and C# require a VM. D (like Go) generate native binaries.
As such, D is a really great choice when you need to write glue code around native libraries. D makes it easy, the calls are low overhead, and there isn't much need for data marshaling and un-marshaling because the data type representations are consistent. D has lower cognitive overhead, more guardrails (which are useful when quickly prototyping code), and a faster / more convenient compile-debug loop, especially wrt to C++ templates versus D generics.
2. Java and C# can also generate native binaries, just like Go, no need for VM.
3. C++ nowadays has concepts, modules and compile time execution
This wasn't true in 2010, but D has let them catch up with missing features.
There were several vendors selling AOT compilers for embedded systems, nowadays they are concentrated into two vendors, PTC and Aicas.
Then you have the free beer compilers GraalVM and OpenJ9, which are basically the reason why companies like Excelsior JET ended up closing shop.
Also .NET has had many flavours, starting with NGEN, Mono AOT, Bartok, MDIL, .NET Native, and nowadays Native AOT.
Both ecosystems are similar to Lisp/Scheme nowadays, having a mix of JIT and AOT toolchains, each with its plus and minus, allowing the developers to pick and choose the best approach for their deployment scenario.
On #2, I generally reach for either TS/JS with Deno if I need a bit more than a shell script, or Rust for more demanding things. I like C# okay for the work stuff that I do currently though.
But I think Walter quite like what D is today.
But by including the GC/runtime it went into a category with C# and Java which are much better options if you're fine with shipping a runtime and GC. Eventually Go showed up to crowd out this space even further.
Meanwhile in the C/C++ replacement camp there was nothing credible until Rust showed up, and nowadays I think Zig is what D wanted to be with more momentum behind it.
Still kind of salty about the directions they took because we could have had a viable C++ alternative way earlier - I remember getting excited about the language a lifetime ago :D