Anyway, here's few videos of interest:
https://www.youtube.com/watch?v=WwkuAqObplU
https://www.youtube.com/watch?v=IroPQ150F6c
Odin also supports SoA natively https://odin-lang.org/docs/overview/#soa-data-types
11–20 of 23 posts
Anyway, here's few videos of interest:
https://www.youtube.com/watch?v=WwkuAqObplU
https://www.youtube.com/watch?v=IroPQ150F6c
Odin also supports SoA natively https://odin-lang.org/docs/overview/#soa-data-types
Turns out, this works completely fine in Firefox. However, in Chrome, it produces millions of individual HeapNumber allocations (why is that a thing??) in addition to the objects and uses GBs of RAM, and is slow to access, making the whole thing unusable.
Replacing it with a SoA structure using TypedArray made it fast in both browsers and fixed the memory overhead in Chrome.
As someone more familiar with systems programming than web, the concept of creating individual heap allocations for a single double baffles me beyond belief. What were they thinking?
I had a similar problem when I was making a tool processing a lot of data in the browser. I'd naively made a large array of identical objects each holding a bunch of fields with numbers. Turns out, this works completely fine in Firefox. However, in Chrome, it produces millions of individual HeapNumber allocations (why is that a thing??) in addition to the objects and uses GBs of RAM, and is slow to access, making the…
Spidermonkey uses (I'm simplifying here, there are cases where this isn't true) a trick where all values are 64-bits, and for anything that isn't a double-precision float they smuggle it inside of the bits of a NaN. This means that you can store a double, a float32, an int, or an object pointer all in a field of the same size. Great, but creates some problems and complications for asm.js/wasm because you can't rely on all the bits of a NaN surviving a trip through the JS engine.
V8 instead allocates doubles on the heap. I forget the exact historical reason why they do this. IIRC they also do some fancy stuff with integers - if your integer is 31 bits or less it counts as a "smi" in that engine, or small int, and gets special performance treatment. So letting your integers get too big is also a performance trap, not just having double-precision numbers.
EDIT: I found something just now that suggests Smis are now 32-bits instead of 31-bits in 64-bit builds of v8, so that's cool!
Firefox
AoS: 2951.00ms
SoA: 1624.00ms
Interleaved: 1961.00ms
Chrome AoS: 2133.30ms
SoA: 884.30ms
Interleaved: 1457.60ms
Seems the interleaved being slower is consistent across browsers!I had a similar problem when I was making a tool processing a lot of data in the browser. I'd naively made a large array of identical objects each holding a bunch of fields with numbers. Turns out, this works completely fine in Firefox. However, in Chrome, it produces millions of individual HeapNumber allocations (why is that a thing??) in addition to the objects and uses GBs of RAM, and is slow to access, making the…
Yeah, this is a historical design difference between Firefox's Spidermonkey JS engine and Chrome's V8. Spidermonkey uses (I'm simplifying here, there are cases where this isn't true) a trick where all values are 64-bits, and for anything that isn't a double-precision float they smuggle it inside of the bits of a NaN. This means that you can store a double, a float32, an int, or an object pointer all in a field of the…
IMHO the bigger issue with NaN-boxing is that on 64-bit systems it relies on the address space only needing On the other hand, I love the fact that NaN-boxing basically lets you eliminate all heap allocations for doubles.
I actually wrote a small article a while back on a hybrid approach called Ex-boxing (exponent boxing), which tries to get at the best of both worlds: decouple the boxing representation from virtual address significant bits, and also represent most (almost all) doubles that show up at runtime as immediates.
https://medium.com/@kannanvijayan/exboxing-bridging-the-divi...
ie `FancyList` would internally create a list for every field of `Point` and reconstruct appropriately when indexing FancyList.
Earlier quoted context omitted.
Much of a difference from which, the array of objects or an object containing arrays? The article points out at least one major optimization that the runtime performs on arrays that doesn't (and as I understand it, can't) exist for objects. My point is that it's not obvious whether there are others, and if so, where they might apply. Pretty much the entire last paragraph of my comment that you responded to is an argu…
I mean if you replaced an array of one million objects with an array of one million arrays you’d probably end up with similar performance. The article is discussing how you get better performance from having arrays with one million primitives. It’s not at all surprising that this is faster.
Once again, my argument is that I think there's evidence against making assumptions like "you'd probably end up with similar performance" and that actually testing assumptions like this is worthwhile. I'm not sure how I could make this more clear at this point though, so I doubt it's worth it for me to try to spend more time understanding whether you don't understand what I'm suggesting or are just unwilling to explain why you disagree with it.
That feels sufficiently intuitive that describing it as "a JavaScript performance issue" is a bit confusing.
(There's other optimizations they're applying, but that's the only one that really matters.)
Earlier quoted context omitted.
Yeah, this is a historical design difference between Firefox's Spidermonkey JS engine and Chrome's V8. Spidermonkey uses (I'm simplifying here, there are cases where this isn't true) a trick where all values are 64-bits, and for anything that isn't a double-precision float they smuggle it inside of the bits of a NaN. This means that you can store a double, a float32, an int, or an object pointer all in a field of the…
I suspect it's just circumstantial - two different design approaches. Both of the approaches have their advantages and disadvantages. IMHO the bigger issue with NaN-boxing is that on 64-bit systems it relies on the address space only needing On the other hand, I love the fact that NaN-boxing basically lets you eliminate all heap allocations for doubles. I actually wrote a small article a while back on a hybrid approa…
* when running the script with Node.js, the results are inline with the article (SoA is the fastest)
* Bun is slower than Node.js with both SoA and AoS.
* Bun has similar performance between SoA and AoS.
* in Bun, Interleaved is the fastest one by a significant margin. This is consistent through runs.
% bun bench.js
AoS: 924.54ms
SoA: 1148.57ms
Interleaved: 759.01ms
Bun's performance profile seems very different from Firefox and V8-based runtimes there. I wonder how QuickJS would fare. The article didn't mention the CPU used either, the performance difference may be dependent on the architecture as well.