Faster Lua VM: Studio beta

There’s one thing I noticed that changed in the new VM, which was the complete destruction of the performance of next. I’m not sure why you wouldn’t optimize both, or even just next, since pairs only serves one use, which is iterating through a table, but next can be used to iterate through a table like pairs, but also can be used outside of that.

Take for example this code, which uses pairs for iterating, but I am stuck with next to see if the loop continues, which is inconsistent and slower.

To me, it just makes no sense, why optimize one but not the other? I get you’re optimizing for idiomatic code, but that doesn’t mean you should leave some things slower than others just because they don’t match your code style guide or whatever. Some people like next because of consistency (like me) reasons like this. It’s just annoying because I’m going to have to back and change any cases of this being done with much uglier code than just doing next would do. It just feels really, really lopsided and I don’t really think it should be done this way. If I was to choose, I’d just keep pairs unoptimized, since it is just a one trick pony.

/rant over

2 Likes

Because pairs is translated into VM instructions that don’t call an iterator function on every cycle. It’s said that they might do the same with next if many people ask, but it’s far at the botom of their to-do list.

1 Like

We’ve been over this earlier in the thread. next actually isn’t slower. It’s just as fast as any other function. Don’t confuse pairs being fast with next being slow.

6 Likes

When you say there isn’t any inter-module analysis does that mean, relating to getfenv/setfenv, optimizations are only disabled on the script or module which contains the call to getfenv/setfenv? If a such a module requires another module, is that second module effected even if it doesn’t contain getfenv/setfenv?

If a script uses getfenv on a function received from a module, will that function have optimizations disabled?

Consider the following module:

return function()
    setfenv(2, {ok = 1})
end

Any script that requires this is potentially unsafe to optimize, and not only that but that script can then pass the function around to who knows where and suddenly everything becomes much harder to reliably optimize.

Now I don’t work for Roblox, but my guess would be the effect would be global, or at least heavily propagated in some way.

1 Like

Note, in a version that will ship next week, loops with next (e.g. for k,v in next, table) and loops with localized ipairs/pairs (e.g. local pairs = pairs) will also benefit from the loop iteration optimization.

Standalone calls to next are not specialized in any way for now, this may or may not happen as part of upcoming builtin function call improvements.

9 Likes

@zeuxcg If we could get precise details on this, that would be great

getfenv/setfenv dynamically deoptimizes some optimizations applied to scripts based on the argument.

For example, if you retrieve the env. table of a function declared in a ModuleScript via getfenv, any functions in that ModuleScript will be deoptimized.

As a general rule of thumb, the deoptimization will be applied to functions that share the same environment table. So if you get/set the env. table in a script, only functions from that script will be affected.

5 Likes

So if I did something such as…

getfenv().print = 2

I would assume optimizations would be lost

What I am slightly confused by is if accessing a calling environment without making any changes such as

local env = getfenv(2)

will make the script lose optimizations.

It would be great if a script reading an env. table without ever writing to it was allowed to keep the optimizations. But as it’s been described, this is not the case.

1 Like

Will assigning a library’s member to a variable or table cause any issues? I’m currently storing all global functions inside a table to keep my code clean in non-performance critical scripts.

An example:

local global = {abs = math.abs}--will this in any way affect the next line?
local val = math.abs(-5)

Will this increase the speed of events like .touched as well?

1 Like

This update does not affect physics computations. It’s about improving performance of Lua code.

2 Likes

I’m getting an error when using ResetButtonCallback:

image

repro steps: open a baseplate, put a localscript in starterplayerscripts and use ResetButtonCallback:

game.StarterGui:SetCore("ResetButtonCallback", function()
	print "hello!"
end)

This is unrelated to the new VM, I’ll ping the engineer who implemented this.

1 Like

I temporarily disabled this error message locally and ran the code, it produces this output:

17:52:45.916 - ResetButtonCallback must be set to a BindableEvent or a boolean

The change unfortunately causes an error before we can get to this warning message, so when this situation happens it is less clear what actually went wrong.

2 Likes

I’d love to see games push this new VM to its limit.

1 Like

In the new Lua vm.
This way of calling a method in a Instance: Instance( method name )
How I used the method: game(“GetService”, “Players”)

Basically, another wall to call a method, but with more control and freedom.
Sadly, this was broken. Instead of the method doing what it is suppose to do. It errors.

attempt to call a userdata value

So, can we keep this feature. So that we don’t have to use loadstring or some other hidden methods of calling a method in Instance. Thanks!

I don’t think you have read the thread correctly. This wasn’t a feature at all it was a accidental result with their old __namecall.

I don’t believe they are going to be keeping it since it was purely accidental but who knows.

The way you propose is worlds hackier than proper solutions. I assume you mean dynamically calling methods (for whatever use case).

Try doing instance[method_name](instance, params...).
Like GetGlobals said, it was an accidental “feature”.

3 Likes