Live data from Hacker News

Nova: A JavaScript and WebAssembly engine written in Rust

trynova.dev

31–40 of 70 posts

Re: Nova: A JavaScript and WebAssembly engine written in Rust

#31

Earlier quoted context omitted.

I've seen this brought up a couple times now, but I never get it. Why would ECS fit a JS engine? The ECS pattern optimizes for iterating over ton of data, but a JS engine does the opposite of that, it need to interpret instruction by instruction which could access random data.

Indeed, there's no guarantee that it will fit: I think it will but I don't know and want to find out. There are strong (IMO) reasons to think it will fit, though. User code can indeed do whatever but it rarely does. Programs written in JS are no less structured and predictable than ones written in C++ or Rust or any other language: they mostly operate on groups of data running iterations, loops, and algorithms over a…

Disclaimer: I’m way out of my depth on the theoretical front, despite similarly taking interest in ECS in unconventional places. I’m responding from the perspective of most of my career being in JS/TS.

I think your instincts about program structure are mostly right, but the outliers are pretty far out there.

I’m much less optimistic about how you’re framing arbitrary data access. In my experience, it’s very common for JS code (marginally less common when authored as TS) to treat JSON (or other I/O bound data) as a perpetual blob of uncertainty. Data gets partially resolved into program interfaces haphazardly, at seemingly random points downstream, often with tons of redundancy and internal contradictions.

I’m not sure how much that matters for your goals! But if I were taking on a project like this I’d be looking at that subset of non-ideal patterns frequently to reassess my assumptions.

Re: Nova: A JavaScript and WebAssembly engine written in Rust

#32

OP, since you're here in the comments can you talk about the binary and memory size and sandboxing support? Ability to import and export functions/variables across runtime boundaries? Is this a feasible replacement for Lua scripting in a rust application?

Hmm, sorry, I'm not sure what you mean. The engine is written with a fair bit of feature flags to disable more complicated or annoying JS features if the embedder so wants: it is my aim that this would go quite deep and enable building a very slim, simple, and easily self-optimising JS engine through this. That could then perhaps truly serve as an easy and fast scripting engine for embedding use cases.

That answers half my question (eg disable networking), thank you. The other part was about the overhead of adding this to an app (startup memory usage and increase in binary size) and how much work has been done on interop so that you can execute a static rust function Foo() passing in a rust singleton Bar, or accessing properties or methods on a rust singleton Baz, i.e. calling whitelisted rust code from within the JS env (vice-versa is important but that’s possible by default simply by hard-coding a JS snippet to execute, though marshaling the return value of a JS function without (manually) using JSON at the boundary is also a nice QOL uplift).

Re: Nova: A JavaScript and WebAssembly engine written in Rust

#33

OP, since you're here in the comments can you talk about the binary and memory size and sandboxing support? Ability to import and export functions/variables across runtime boundaries? Is this a feasible replacement for Lua scripting in a rust application?

Hmm, sorry, I'm not sure what you mean. The engine is written with a fair bit of feature flags to disable more complicated or annoying JS features if the embedder so wants: it is my aim that this would go quite deep and enable building a very slim, simple, and easily self-optimising JS engine through this. That could then perhaps truly serve as an easy and fast scripting engine for embedding use cases.

> written with a fair bit of feature flags

I see you use Cargo feature for this. One thing to be aware of is Cargo's feature unification (https://doc.rust-lang.org/cargo/reference/features.html#feat...), ie. if an application embeds crate A that depends on nova_vm with all features and crate B that depends on nova_vm without any security-sensitive features like shared-array-buffer (eg. because it runs highly untrusted Javascript), then interpreters spawned by crate B will still have all features enabled.

Is there an other way crate B can tell the interpreter not to enable these features for the interpreters it spawns itself?

Re: Nova: A JavaScript and WebAssembly engine written in Rust

#34
post #33

Earlier quoted context omitted.

Hmm, sorry, I'm not sure what you mean. The engine is written with a fair bit of feature flags to disable more complicated or annoying JS features if the embedder so wants: it is my aim that this would go quite deep and enable building a very slim, simple, and easily self-optimising JS engine through this. That could then perhaps truly serve as an easy and fast scripting engine for embedding use cases.

> written with a fair bit of feature flags I see you use Cargo feature for this. One thing to be aware of is Cargo's feature unification ( https://doc.rust-lang.org/cargo/reference/features.html#feat... ), ie. if an application embeds crate A that depends on nova_vm with all features and crate B that depends on nova_vm without any security-sensitive features like shared-array-buffer (eg. because it runs highly untrus…

Nice catch, thanks for pointing that out! This also might be less than ideal if it’s the only option (rather than in addition to a runtime startup flag or a per-entrypoint/execution flag) because one could feasibly want to bundle the engine with the app with features x, y, and z enabled but only allow some scripts to execute with a subset thereof while running different scripts with a different subset.

Re: Nova: A JavaScript and WebAssembly engine written in Rust

#35

Earlier quoted context omitted.

I've seen this brought up a couple times now, but I never get it. Why would ECS fit a JS engine? The ECS pattern optimizes for iterating over ton of data, but a JS engine does the opposite of that, it need to interpret instruction by instruction which could access random data.

Indeed, there's no guarantee that it will fit: I think it will but I don't know and want to find out. There are strong (IMO) reasons to think it will fit, though. User code can indeed do whatever but it rarely does. Programs written in JS are no less structured and predictable than ones written in C++ or Rust or any other language: they mostly operate on groups of data running iterations, loops, and algorithms over a…

Hmm, a compacting garbage collector that would try to put live data together, according to its access patterns, might be fun to consider. Along these lines, it could even split objects' attributes along ECS-friendly lines, working in concert with a profiler.

Re: Nova: A JavaScript and WebAssembly engine written in Rust

#36

Earlier quoted context omitted.

I've seen this brought up a couple times now, but I never get it. Why would ECS fit a JS engine? The ECS pattern optimizes for iterating over ton of data, but a JS engine does the opposite of that, it need to interpret instruction by instruction which could access random data.

Indeed, there's no guarantee that it will fit: I think it will but I don't know and want to find out. There are strong (IMO) reasons to think it will fit, though. User code can indeed do whatever but it rarely does. Programs written in JS are no less structured and predictable than ones written in C++ or Rust or any other language: they mostly operate on groups of data running iterations, loops, and algorithms over a…

I had the impression, ECS would boost performance mainly by allowing the systems to run in parallel on the entities. Isn't this kinda moot in a single threaded runtime?

Re: Nova: A JavaScript and WebAssembly engine written in Rust

#37
post #15
post #7

Hi, main developer of Nova here if you want to ask any questions! I'm at a choir event for the rest of the week though, so my answers may tarry a bit.

FYI I'm getting an SSL certificate error trying to load the site.

It's hosted by GitHub pages with Cloudflare DNS so any issues are probably related to that.

Re: Nova: A JavaScript and WebAssembly engine written in Rust

#38
post #28

Earlier quoted context omitted.

Haha, I think that was a one time fluke! :D I'm aiming for something like 75-85% this year; basically get iterators properly done (they're in the engine but not very complete yet), implement ECMAScript modules, and then mostly focus on correctness, builtins, and performance improvements after that. 99% would perhaps be possible by the end of next year, barring unforeseeable surprises.

have you considered using js polyfills to help you get closer to 100% coverage and then replacing with native implementations prioritized by performance impact?

Not really, no. Its an interesting proposition, but for the most part I believe I'll be sticking it out the "hard way". The ECMAScript spec is fairly easy to read as well, after all. (Nevermind that I spent the single free hour I had today cursing at my incapability of understanding what is going wrong with my iterator code and what it even should do vis-à-vis the spec :D )

Re: Nova: A JavaScript and WebAssembly engine written in Rust

#39

Earlier quoted context omitted.

Indeed, there's no guarantee that it will fit: I think it will but I don't know and want to find out. There are strong (IMO) reasons to think it will fit, though. User code can indeed do whatever but it rarely does. Programs written in JS are no less structured and predictable than ones written in C++ or Rust or any other language: they mostly operate on groups of data running iterations, loops, and algorithms over a…

Disclaimer: I’m way out of my depth on the theoretical front, despite similarly taking interest in ECS in unconventional places. I’m responding from the perspective of most of my career being in JS/TS. I think your instincts about program structure are mostly right, but the outliers are pretty far out there. I’m much less optimistic about how you’re framing arbitrary data access. In my experience, it’s very common fo…

Hey, thank you for the viewpoint. I'm myself a career JS/S programmer as well, and I do appreciate that the lived reality is quite varied.

The partial resolving and haphazardness of JSON data usage shouldn't matter too much. I don't mean to make JSON parsed objects to be some special class, per se, or for the memory layout to depend on access patterns on said data. Only, I force data that was created together to be close together in memory (this is what real production engines already do, but only if possible) and for that data to stay together (again, production engines do this but only as is reasonably possible; I force the issue). So I explicitly choose temporal coherence. Beyond that, I use interface inheritance / removal of structural inheritance to reduce memory usage. eg. Plain Arrays (used in the common way) I can push to 9 bytes or even 8 bytes if I accept that Arrays with a length larger than 2^24 are always pessimised. ECS / Struct-of-Arrays data storage then further allows me to choose to move some data onto separate cache lines.

But; it's definitely true that some programs will just ruin all reasonable access patterns and do everything willy-nilly and mixed up. I expect Nova to perform worse on those kinds of cases: as I am adding indirection to uncommon cases and splitting up data onto multiple cache lines to improve common access patterns, I do pessimise the uncommon cases further and further down the drain. I guess I just want to see what happens if I kick those uncommon cases to the curb and say "you want to be slow? feel free." :) I expect I will pay for that arrogance, and I look forward to that day <3

Re: Nova: A JavaScript and WebAssembly engine written in Rust

#40
post #35

Earlier quoted context omitted.

Indeed, there's no guarantee that it will fit: I think it will but I don't know and want to find out. There are strong (IMO) reasons to think it will fit, though. User code can indeed do whatever but it rarely does. Programs written in JS are no less structured and predictable than ones written in C++ or Rust or any other language: they mostly operate on groups of data running iterations, loops, and algorithms over a…

Hmm, a compacting garbage collector that would try to put live data together, according to its access patterns, might be fun to consider. Along these lines, it could even split objects' attributes along ECS-friendly lines, working in concert with a profiler.

Nova's GC doesn't use access patterns for this, but this is basically what we do, or in some cases aim to do.

Arrays, Objects, ArrayBuffers, Numbers, Strings, BigInts, ... all have their data allocated onto different heap vectors. These heap vectors will eventually be SoA vectors to split objects' attributes along ECS-friendly lines; eg. Array length might be split from the elements storage pointer, Object shape pointer split from the Object property storage pointer etc. Importantly, what we already do is that an Array does not hold all Object's attributes but instead holds an optional pointer to a "backing Object". If an Array is used like an Object (eg. `array.foo = "something"`) then a backing object is created and the Array's backing Object pointer is initialised to point to that data. Because we use a SoA structure, that backing Object pointer can be stored in a sparse column, meaning that Arrays that don't have a backing Object initialised also do not initialise the memory to hold the pointer.

I'm also interested in maybe splitting Object properties so that they're stored in ECS-friendly lines (at least if eg. they're Objects parsed from an Array in JSON.parse).

Our GC is then a compacting GC on these heap vectors where it simply "drops" data from the vector and moves items down to perform compaction. This also means it gets to perform the compaction in a trivially parallel manner <3

Post reply on HN