Our new Lua VM, Luau, is now, as of ~1h ago, live on all platforms for client and server for all games, and additionally live in Studio for plugins.

What is it?

As the platform grows, we see more and more use of Lua and as a result, it’s more and more important to make sure Lua scripts run efficiently. We’ve spent some time over the last few years optimizing our reflection layer - the system that allows Lua scripts to communicate to the engine - but we’ve never done anything about Lua itself.

With the goal of having Lua run faster on all our platforms, we’ve evaluated a set of approaches and decided to build a new Lua implementation because we’re crazy . We have written a brand new compiler and a brand new interpreter, and we’ve made a few changes to the reflection layer and garbage collector, but we haven’t changed the standard library, so depending on the performance characteristics of your code you will see varied gains. Reflection access got a bit faster in both old VM and new VM as a result of this work as well.

In usual scripts we see a mix of reflection access and other work - depending on what the scripts do you can get all the way between “it’s not faster at all” and “it’s several times faster” as a result. Our builtin terrain generator is about 2x faster with new VM, and some scripts we’ve looked at are up to 3x faster.

What about Studio?

We are not fully ready to switch to the new VM in Studio because we don’t have the debugger fully operational. However, the new VM has large performance improvements both in terms of code execution time, and in terms of join time (smaller bytecode => less data to send to the client during join => faster joins!). Additionally, it breaks many exploits because of the new bytecode structure but also because we no longer embed local variable names in the bytecode. (worth noting is that fundamentally the VM isn’t more secure aside from that and we expect exploiters to catch up)

These improvements are large enough that we decided to run the VM in contexts where the debugger isn’t necessary before we’re fully ready with the debugger support. If you want to run the new VM everywhere - which basically means “during testing in Studio” - and if you don’t use the debugger right now, you can use the Beta Features list in File menu to activate it:

We’re working on the debugger support and when it’s ready, we will automatically enable new VM in test modes in Studio as well, at which point the old VM will not exist on the platform anymore.

What’s next for Lua performance?

Due to the staged nature of our updates, we have three more performance improvements ready to go but waiting for mobile clients to catch up - in a few weeks time, calls to many builtin functions like math.abs , math.max , bit32.rshift et al will become substantially faster (we’re seeing ~2x performance improvement on an SHA256 benchmark for example), power operator (x^k) will become much faster for commonly uses powers such as 2 and 0.5, and reflection access to properties will become noticeably faster for objects with a long ancestry chain (e.g. we’re seeing ~2x faster reads of NumberValue.Value when NumberValue parent list has 8 instances in it).

Of course we want to keep improving the performance of our VM - if you have scripts that you’re not happy with performance of, feel free to share them with us which in some cases can lead to VM becoming faster. We already have a list of optimizations we want to implement, including closely integrated performant support for Vector3 math, improved performance for closure creation for non-mutable upvalues, better optimizations for table constructors and more.

We’re also looking into a way to unlock access to multiple cores. As I mentioned during my RDC talk (which you’re all welcome to watch! https://www.rdcglobal.com/video-stream-gallery/lua-as-fast-as-possible-rdc-2019), we think we have a design that will allow you to run Lua code on multiple threads safely and performantly, which could unlock performance for some specific usecases that just isn’t achievable right now.