Earlier quoted context omitted.
Maybe I'm missing something but when would the 500 geese instances ever be stack allocated in Rust? That comparison seems unfair, the lifetime of that kind of object isn't going to be compatible with stack allocation. Allocations are really really cheap in Java by the way, so I don't get how 500 allocations would even be an issue.
When you make a local variable with a Goose in Java, that's a heap allocation Goose jim = make_a_goose_somehow(); // Java, so jim is on the Heap, no way around it When you make that variable in Rust... let jim : Goose = make_a_goose_somehow(); // Rust, jim is on the stack Now, if we make a bunch of geese, maybe in a loop, and we put them into our growable array type... ArrayList geese = new ArrayList (); // ... some…
Understanding the Odin programming language
91–100 of 163 posts
Re: Understanding the Odin programming language
#92Earlier quoted context omitted.
When you make a local variable with a Goose in Java, that's a heap allocation Goose jim = make_a_goose_somehow(); // Java, so jim is on the Heap, no way around it When you make that variable in Rust... let jim : Goose = make_a_goose_somehow(); // Rust, jim is on the stack Now, if we make a bunch of geese, maybe in a loop, and we put them into our growable array type... ArrayList geese = new ArrayList (); // ... some…
This isn't necessarily true - at least not in a meaningful way - if Goose has a Vec in it, that'll end up on the heap no matter what.
Fine. If we put a growable array type inside Goose the backing store for such a type would [if needed] live on the heap, unfortunately in Java we're not allowed to use a primitive type like the signed integer here, so lets put a growable array of Doodads where each Doodad is 64 bits.
So now each Java Goose has an ArrayList and yup, each Doodad goes on the heap, and then the ArrayList's backing store goes on the heap with pointers to the Doodads, and then the rest of the Goose goes on the heap too.
In Rust we skip most of those steps for Vec but if there's at least one Doodad we do need to actually have the backing store, the Doodads live directly in that backing store.
Of course if there aren't any Doodads, Java still pays to make the backing store anyway, that's just how Java works again. Rust doesn't do that, an empty Vec is just a dangling pointer, zero capacity, zero length. Since the length and capacity are zero we'll never dereference the pointer.
The story doesn't get better for Java if you make it more convoluted, that's not how any of this works.
Re: Understanding the Odin programming language
#93Earlier quoted context omitted.
Casey spends a lot of effort on what he considers an anti-pattern where you're making a huge number of separate objects. I think a lot of this comes from Java, a language where all the user defined types actually are obliged to be heap allocations. If I make a Goose type, and I say I want a Goose, Java will allocate space on the heap for the Goose and put my Goose there, that's really how Java works. If I make a grow…
Actually, this is my personal opinion, but in Java, allocating an object is just bumping a pointer, so it's much cheaper than doing a malloc in C. I personally don't think the stack is a good place to put temp values and I'm sure neither does Jon Blow, considering he put temporary arena backed allocators right in the language.
Local variables don't somehow live in this arena allocator in Odin or Zig, and presumably (I haven't used it) not in Jai either. The obvious place for a local to live is the stack - unless you're Java and you need all your user defined types to live on the heap so that they have unique identities. So that's where locals live in Rust, Zig, Odin, C, C#, Go ...
Re: Understanding the Odin programming language
#94Earlier quoted context omitted.
You're correct that by an "as if" rule the JIT may in some cases be able to do this trick, but the specification says you're getting a heap allocation and so in most cases, including the example I gave that's exactly what happens. It doesn't matter that you say this is "much closer to something like using an arena". It's definitely work that we needn't do at all and that's AFAICT that's how Casey ends up over-correct…
What "work" do we do needlessly? It's a pointer bump. With reclamation being no work. Comparatively a malloc call will have to do extra work to defragment. Also, a stack is not a separate hardware element of the RAM, it's not faster than a sufficiently hot part of the heap - it just so happens that the current stack frame is pretty likely to be in cache.
Any work here was needless. There was no need to do work.
Re: Understanding the Odin programming language
#95Earlier quoted context omitted.
Pedantically I’ll say it’s reference counted, and someone else will say that’s still a form of GC and I’ll just save us the mini-thread. Reference counting has deterministic timing, you can run a deconstructor without registering objects for deletion and running any known finalizers (what you need to do in all GC langs I’m aware of.)
> Reference counting has deterministic timing Define “deterministic timing”. - One object going out of scope may mean calling free once, but it also can trigger calling free billions of objects, even for the exact same object - Even freeing one object can have largely varying running time, e.g. to coalesce free blocks or, because it happens to be the last block in a virtual memory region, to unmap a block of virtual…
Re: Understanding the Odin programming language
#96Earlier quoted context omitted.
This isn't necessarily true - at least not in a meaningful way - if Goose has a Vec in it, that'll end up on the heap no matter what.
(Sigh) Fine. If we put a growable array type inside Goose the backing store for such a type would [if needed] live on the heap, unfortunately in Java we're not allowed to use a primitive type like the signed integer here, so lets put a growable array of Doodads where each Doodad is 64 bits. So now each Java Goose has an ArrayList and yup, each Doodad goes on the heap, and then the ArrayList's backing store goes on th…
Re: Understanding the Odin programming language
#97Earlier quoted context omitted.
> Casey Muratori and Jon Blow have pushed this concept frequently Ah... The school of what I like to call "maximum opinions and minimal evidence". Aggressive arrogant dismissal of anything except their exact view (and for Muratori, you're also "woke" for good measure), coupled with a complete lack of _hard evidence_ to back up their views. In that regard, they're not unlike "investment advice" instagram influencers.
Apparently you're the school of dismissive person who can't even be bothered to look up their technical achievements.
Re: Understanding the Odin programming language
#98Earlier quoted context omitted.
From what I understood, their critique of RAII is twofold: coupling of allocation and initialisation, and enforcement of deallocation. The ease of use of smart pointers makes it tempting to allocate/free of temporary structures even within one single function. Given enough number of such occurrences, it kills performance by a thousand cuts. Also I remember they mentioned it’s not necessary to free memory if you’re ab…
most of that criticism only works on C++. rust does enforce RAII but uses stack allocation for locals by default instead of touching the heap so it skips the slow part. on the other hand there are no constructors (just normal functions) so you cant initialize values in place, only stack allocate and return. i think rust needs to add in place init and change the rules from "always init at declaration site" to "must be…
Re: Understanding the Odin programming language
#99Earlier quoted context omitted.
> Reference counting has deterministic timing Define “deterministic timing”. - One object going out of scope may mean calling free once, but it also can trigger calling free billions of objects, even for the exact same object - Even freeing one object can have largely varying running time, e.g. to coalesce free blocks or, because it happens to be the last block in a virtual memory region, to unmap a block of virtual…
Well, GC has indeterminate timing in and of itself . Reference counting does not. It can still trigger indeterminate timing if the destructor calls free, or if the "thing" going out of scope is a pointer, but that's on the destructor or the pointer, not on reference counting per se.
It is on reference counting per se. If the reference count of a reference goes to zero, the runtime has to make the memory available for reuse. That’s what I called ‘calling free’.
Also, about “GC has indeterminate timing in and of itself”. Again: define what you mean by it. Yes, The timing of allocating an object can vary depending on the state of your memory allocator, but that’s the same with reference counting.
(even allocating an object on the stack and then writing to it can have variable timing on many OSes. If you look really close, even decreasing a stack pointer can have variable timing on modern pipelined CPUs due to data dependencies)
And as I said, the GC work to find and reclaim unreachable objects can run on separate thread(s).
Re: Understanding the Odin programming language
#100Earlier quoted context omitted.
Pedantically I’ll say it’s reference counted, and someone else will say that’s still a form of GC and I’ll just save us the mini-thread. Reference counting has deterministic timing, you can run a deconstructor without registering objects for deletion and running any known finalizers (what you need to do in all GC langs I’m aware of.)
> Reference counting has deterministic timing Define “deterministic timing”. - One object going out of scope may mean calling free once, but it also can trigger calling free billions of objects, even for the exact same object - Even freeing one object can have largely varying running time, e.g. to coalesce free blocks or, because it happens to be the last block in a virtual memory region, to unmap a block of virtual…
- Even freeing one object can have largely varying running time, e.g. to coalesce free blocks or, because it happens to be the last block in a virtual memory region, to unmap a block of virtual memory, blocking the program potentially for an arbitrary time"
So, like C, C++, or anything else that you wouldn't call garbage collected.