From Wikipedia article of each language:
Rust: First appeared July 7, 2010; 9 years ago
Go: First appeared November 10, 2009; 9 years ago
Nim: First appeared 2008; 11 years agoFrom Wikipedia article of each language:
Rust: First appeared July 7, 2010; 9 years ago
Go: First appeared November 10, 2009; 9 years ago
Nim: First appeared 2008; 11 years agoThat's a big advantage over Rust, D, OCaml, and similar languages without this feature.
Ultimately if you're really interested in performance, regardless of the stages of compilation, the juice is the machine code output at the end.
In terms of guarantees, those should be satisfied higher up in the language itself, with C generation being output according to Nim's CGen spec. The compiler is fully open source though and easy to dig into.
Having said that, the CGen output is fairly readable if you're familiar with C and I must say I've investigated it when I wasn't sure how something was generated.
Those languages tend to be a lot simpler than C, both in terms of what constructs they offer and in terms of how simply they translate into (platform-specific) assembly language. I'm not against the idea of intermediate languages in general, but IME C is the worst of both worlds: more complicated than most high-level languages and most assembly languages.
> In terms of guarantees, those should be satisfied higher up in the language itself, with C generation being output according to Nim's CGen spec. The compiler is fully open source though and easy to dig into.
The problem is being confident that those guarantees are preserved all the way down. It's very hard to be confident of the properties that any given piece of C code has, because it's extremely rare for C code to be 100% standards compliant and different compilers do radically different things with the same code. And it's hard to reason about the effects of changes because C compilers are so complicated: maybe you understand the Nim compiler and can see how two similar Nim functions will generate similar C code, but that doesn't help you much when two similar C functions can be compiled to radically different assembly (which happens a lot).
Indeed several languages compile to C; I regard that as a downside for all those languages. I'm not sure what your point is?
If you care about the assembly a piece of code generates, you can just look at that.
The generated C code that Nim emits looks like what it is: boring automatically-generated C code.
If you wanted to debug the compiler, you could take a look at that, but most users don't have to.
type
# A `ref` type is GC and will use the heap.
MyGCType = ref object
fieldA: int
# Otherwise ALL types are stack based.
MyStackType = object
fieldA: int
Also GC is deferred, so if you use a GC type in a local scope that doesn't escape, you don't pay for reference counting.The only other type that use GC is `seq` (equivilent to C++ vectors) IIRC.
The standard library uses seqs in various places so if you fully turn the GC off using the compiler switch --gc:none you'll get warnings for things you use that will leak. There's no GC 'runtime' stuff that you need though.
However in my experience all you need to do is just not use refs, and make your own ptr based seq (there's probably a library for this but its trivial to implement).
Nim's GC is thread-local (so no stop-the-world issues), only triggered on allocate, and has realtime support via enforcing collection periods. Plus you can use other GCs if you wish (eg Boehm).
More info about the GC here: https://nim-lang.org/docs/gc.html
So similar to C# (2000)? A useful feature to be sure, but not a major innovation.
> The standard library uses seqs in various places so if you fully turn the GC off using the compiler switch --gc:none you'll get warnings for things you use that will leak. There's no GC 'runtime' stuff that you need though.
Running with the GC off (and accepting the leaks) was already a standard managed-language technique though.
> Nim's GC is thread-local (so no stop-the-world issues)
Well no wonder it has nice properties if it avoids all of the hard problems! What happens when you pass references between threads?
> only triggered on allocate, and has realtime support via enforcing collection periods.
Your link describes the realtime support as best-effort, and implies that it doesn't work for cycle collection. So honestly this doesn't seem to offer much over e.g. the tuneable GC of the JVM (which admittedly made a massive mistake in choosing defaults that prioritised batch throughput rather than latency).
I do appreciate the information, and hope this isn't coming off as overly confrontational. But honestly it sounds like Nim is overselling things that are mostly within the capabilities of existing managed languages (maybe even behind them if the "GC" is only thread-local and/or not cycle-collecting).
Or do you mean something else?
In what concerns C#, besides GC, you get access to off heap unamaged allocations, low level byte manipulations, value types, inlined vector allocations, stack allocation, struct aligments, spans.
All GCs have eventually to stop the world, but they aren't all made alike, and it is up to developers to actually take use of the language features for writing GC-free code.
Not entirely true. Erlang's BEAM definitely doesn't need to (unless you define "world" to be a single lightweight process). Perl6's MoarVM apparently doesn't need to, either.
Doing it in some local context is a way to minimize overall process impact.
Just like reference counting as GC algorithm does introduce pauses, specially if implemented in a naive way. More advanced ones end up being a mini tracing GC algorithm.
Regardless, having any form of automatic memory management in system languages is something that should have already become standard by now.
Erlang doesn't require stopping the world because every Erlang process is isolated from the others (no shared state at all, let alone mutable shared state). I don't know off-hand how Perl avoids it.
Not at all. Nim is an example.
The language does have pointers, and will let you do any C-style memory unsafe whatever you want to do with them. However, it doesn't have any library calls that I'm aware of that are equivalent to C's malloc() and free(). You'd have to supply your own.
There are also ambitions, of introducing real non-GC memory-safe memory management. Probably something along the lines of how Rust does it. Those haven't come to fruition yet, though.
So, long story short, yes you can completely disable GC, but I think that its capabilities on that front are somewhat overstated.
The malloc/free equivilents are in the system module: https://nim-lang.org/docs/system.html#alloc%2CNatural
GC types are copied over channels with threads, or you can use the usual synchronisation primitives and pass pointers.
As you say, thread-locality avoids the hard problems and this is a good default - I would argue that most of the time you want your data being processed within a thread and the communication between them to be a special case.
Certainly, there's a lot of talk of adding some sugar to threading, and Nim does offer some interesting tastes, such as the parallel statement: https://nim-lang.org/docs/manual_experimental.html#parallel-...
The performance of the default GC is good to very good, the JVM is almost certainly better in most cases, however this is comparing apples to oranges; it's a different language.
Nim's GC pressure is much lower in most cases, not least because everything defaults to stack types which not only don't use GC but tend to be much better for cache coherence due to locality. Using ref types is not required unless you use inheritance, however the language does encourage composition over inheritance, despite providing full OO capabilities, so you find inheritance isn't as needed as in other languages.
Plus it's not really much different to using refs to drop down to pointer level and avoid the GC without disabling it:
type
DataObj = object
foo: int
Data = ptr DataObj
proc newData: Data = cast[Data](alloc0(DataObj.sizeOf))
proc free(data: Data) = data.deAlloc
var someData = newData()
someData.foo = 17
echo someData.repr
# the echo outputs eg: ptr 000000000018F048 --> [foo = 17]
someData.free
All this means that you can 'not use the GC' whilst not disabling it. I am a performance tuning freak and I still use GC seqs all the time because the performance hit is actually using the heap instead of the stack, and worse, the actual allocation of heap memory - regardless of the language. The GC overhead is miniscule even with millions of seqs and would only even come into play when allocating memory inside loops. At that point, it's not the GC that's an issue, but the allocation pattern.Again though, it's nice to be able to drop down to pointers easily when you do need every clock cycle.
Languages like Nim allow for having the productivity of relying on GC's help, with language features for performance freaks available, when they really need to make use of them.
Java not offering a Modula-3 like feature set has tainted a whole generation to think that GC == Java GC.
EDIT: grammar errors
We can extend this pattern:
> has tainted a whole generation to think that OOP == Java OOP
To you, that is
I'm lucky to have folks here to set me straight. :)