HaroldCindy
Joined 2 years ago
Joined 2 years ago
They're about the same as before, they weren't terribly large to begin with though. From what I've seen the region crossing issues aren't caused by script state serialization, but hard to track down issues in edge cases in object handoff that're outside the scope of my contract.
It helps a lot that you're only dealing with LSL that was compiled to .NET CIL by a single compiler and transformed into a state machine via an internal tool that predates `async` / `await` in .NET. Luckily you don't need a strategy that works for arbitrary .NET assemblies.
We can inspect those assemblies and the saved script state is stored in an LL-defined serialization format that includes everything on the stack / reachable via the heap. That could be converted to the script state serialization scheme we created for Luau.
The biggest complication would be that .NET CIL presents a stack-based bytecode whereas Lua(u) bytecode is register-based. There's prior art there, IIRC Android's Dalvik bytecode format is register-based and isn't generally compiled directly, you compile stack-based Java bytecode and the Android devkit has tools that convert it to stack-based Dalvik. We could use a similar scheme to convert the limited subset of CIL we need into Luau bytecode, possibly with some Dalvik-like extensions that allow use of "extended" registers for cases where we'd run into Luau's 255 register limit.
I'd like to eventually open-source SL's existing internal tooling for Mono so that people can get a better sense of the problem space and how that conversion would work. It really should have been public from the outset, and I believe the original author of SL's Mono integration wanted it to be.
Migrating existing Mono scripts onto Luau is a bit far out though, since we're still working on the core VM stuff.
We're still in figuring out our async strategy for user-facing APIs to be honest, so these references are super helpful. We already have preemptive scheduling of execution, but it's most likely to be some kind of wrapper around `coroutine.create()` where an event loop is responsible for driving execution flow and internal `coroutine.yield()`s let you specify what you're `await`ing.
We'll likely have an RFC for how that will all work within the year, but several users have written their own bespoke `async` / `await` implementations for SL in Lua already.
We haven't even used Luau's JIT at all yet, but preemptive scheduling of what's typically trivial glue code is much cheaper and easier with a VM that supports it as a natural consequence of its design versus transforming everything into a state machine at the AST or bytecode level for a VM that doesn't.
> Actually, the biggest problem is that each idle program uses about 1us per frame, which adds up.
More scheduler overhead to resolve :)
The scripting engine is an integral part of your product, and you need to "own" it end-to-end. Any bugs that creep into new versions of your scripting engine, any API breakage or design changes that impact your usecase are things that you are responsible for. Roblox owns the entire toolchain for Luau, and it's relatively small compared to the set of libraries required to compile to and execute WASM in a performant way.
The nuances of your typical JITing WASM runtime or V8 are pretty hard to learn compared to a simpler VM like Luau, it's a big reason why I've used Luau in my own projects.
If you look purely at the VM and things necessary to compile bytecode (AST, Compiler and VM) then the difference in code size isn't as stark.
Having worked with both Lua 5.1 and Luau VM code, Luau's codebase is a heck of a lot nicer to work on than the official Lua implementation even if it is more complex in performance-sensitive places. I have mixed feelings on the structural typing implementation, but the VM itself is quite good.
One nit:
> Python programmers often use ad-hoc dictionaries (i.e. maps) to aggregate related information. In Java, we have records:
In modern Python it's much more idiomatic to use a `typing.NamedTuple` subclass or `@dataclasses.dataclass` than a dictionary. The Python equivalent of the Java example:
@dataclasses.dataclass
class Window:
id: int
desktop: int
x: int
y: int
width: int
height: int
title: str
@property
def xmax(self) -> int: return self.x + self.width
@property
def ymax(self) -> int: return self.y + self.height
w = Window(id=1, desktop=1, x=10, y=10, width=100, height=100, title="foo")See https://fuchsia.dev/fuchsia-src/concepts/components/v2/starn... and https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/s...
Yep, to that end they also have a basic bytecode verifier (only used in debug mode / when asserts are enabled) that validates the compiler only outputs valid bytecode, and I believe they continuously fuzz the compiler to make sure those asserts can't be triggered. See https://github.com/luau-lang/luau/blob/0d2688844ab285af1ef52...
It's fairly robust (and Luau bytecode isn't _that_ complex,) but they made the right decision disallowing direct bytecode execution.
PUC-Rio Lua uses C (with a tiny bit of C++ for stack unwinding on errors if folks don't want to use `longjmp()`,) Luau is strictly C++. Luau rewrites quite a lot of the core structures, and they aren't compatible. Further, Luau is a fork of PUC-Rio Lua _5.1_ (the current is 5.4 I think?) and intentionally picks and chooses features from later Lua versions. Even things like improvements to the core VM interpreter loop wouldn't be upstreamable, because Lua 5.2+ has a fundamentally different model to allow for yielding in metamethods. PUC-Rio would not accept changes other than fixes for critical bugs in Lua 5.1.
I like both PUC-Rio Lua and Luau, but a hard fork with no upstreaming was the only option here. The architectural differences of the VMs are so large now that it would amount to PUC-Rio adopting Luau. Supposing there is some tiny bit that PUC-Rio is interested in, they can cherry-pick it out of Luau, since they use the same license.