Luau Recap: November 2019

It’s more performant to use table.create most likely and it’s also much smaller. Example:

-- Previously you'd do this
local tbl = {} -- Empty table
for _=1, 100 do -- _ to void variable. Loop 100 times (100 0s)
	table.insert(tbl, 0) -- Fill table with zeros
end

-- Now you can do this
local tbl = table.create(100, 0) -- This is done in C/C++ so its almost gauranteed to be a bit faster

Speaking of table.create, if you call table.create(n, varThatIsNil) this will just be an empty table. Internally would anything happen if a second argument is not supplied or is nil? Or would an error be thrown?

Edit: Doing a quick little test shows that something does happen. If you call table.create(100000) or table.create(100000, nil) for example your studio will freeze up even though creating a table with nil values is unnecessary since the nil values will be ignored in all cases. Might make sense to return an empty table if the value argument is nil and save some resources even if its rare.

6 Likes

One of the use cases is having the table created at a specific size, to avoid re allocations. Often times when each element wouldn’t be the same.

local t = table.create(1000)
for i=1,1000 do
    t[i] = i
end

Filling it in with nil would be fine in this case.

8 Likes

When I saw the new script analysis warnings, I thought I would have lots of warnings in my game’s large code base… Turns out I only had two!

In the future, I would like to know where I can disable some of these warnings, and I kind of would prefer some of them to be off by default, or have varied settings per place (though I’ll probably keep them on for most projects).

One of the most annoying opinionated warnings for me is the “This statement spans multiple lines; use indentation to silence”, because it can be overbearing in many scenarios. Overall, and with the addition of static type safety, these tools can be powerful, but I hope to see more granular user control coming along with it.

5 Likes

I should mention that we fixed a case where this warning was triggering on idiomatic constructs that involved local declaration e.g.

local foo do
...
foo = value
...
end

In general this warning exists because in some types of code, the expression that overflows to the next line without indentation is naturally read as two separate expressions (because Lua doesn’t enforce statement terminators). This can (and has) lead to confusing bugs.

6 Likes

Just type - not typeof for now. This thread probably is a reasonable place for requests like this - normally a separate thread would be better but it’s a bit easier to track replies to the thread in this case.

2 Likes

There’s a very specific reason for us to not do this. Consider code like this:

local t = {}
for i=1,10000 do
t[i] = i
end

The reason why table.create supports nil is because this code can be rewritten like this for a solid performance win:

local t = table.create(10000)
for i=1,10000 do
t[i] = i
end

In this case table.create preallocates storage for the table without changing its length to make subsequent table assignments faster. Obviously if you don’t know what the size of the table is going to be, preallocation isn’t effective.

10 Likes

what’s the specifics on this
is this common use based or based on a certain bound of exponents

2 Likes

This is based on commonly used exponents, not a range.

3 Likes

Wow, amazing work!

Just one question, will we get a table.map() function to apply a function to every value in the table? Or should we just use loops for now?

4 Likes

Is there any news you can share on multi-threaded Lua execution? Would it allows us to use 100% of the CPU power through Lua?

18 Likes

Alrighty. Well in that case, is there a chance we could get these fastcall optimizations for typeof too? :stuck_out_tongue: I prefer to use it in Roblox for basically the same reason you’d use type (type checking arguments, primarily) so having it be faster would be a plus.

I would also like that sort of optimization on string.byte because it ends up in stuff like hashing or base conversion a bunch. There may not be common enough use to justify it though.

1 Like

Thank you for clearing that up! That does make a lot of sense. The above reply also made me think… Are string functions already using this optimization? Some of my code does tons of string processing (gsub, sub, gmatch, etc). Depending on the size of the content I’m processing I could be making hundreds to hundreds of thousands of string calls.

I wrote a basic compression algorithm which does this and I have had 10-15kb strings that get fed through it which is 10-15k iterations and many more calls.

2 Likes

We had a version of string.byte using this that accelerates our Base64 benchmark, but string.byte is slightly tricky so we haven’t completed the implementation to be production-ready yet.

Please share the code for this with us if you can (PM works) - we can then add this to our benchmark suite, so that we can focus on updates that clearly improve it. It’s hard to tell without testing whether a specific optimization is impactful or not, and we want to carefully expand the builtins because for them to be fast, they by necessity have to replicate the original function’s logic so we need to be careful to keep the behavior exactly identical.

3 Likes

Out of curiosity, do you use typeof as a blanket replacement for type and use it for primitive types a lot, or do you mostly use type for primitive types and typeof for Roblox types? Code examples where you often use typeof would be appreciated - I think our typeof coverage in our benchmark suite is lacking. type specifically improved our Roact benchmark by like 5% because it had a number of type assertions.

3 Likes

Not ready to talk about this yet, sorry - we have not started the implementation. The goal for this project is indeed to allow Lua to use ~100% of CPU power, but there’s going to be caveats and this is going to ship late 2020.

12 Likes

We don’t have plans to implement table.map. Our general policy for new Lua functions is:

  • A function gets implemented if it’s impossible to implement yourself in Lua or the implementation is really involved
  • A function gets implemented if it’s very often used and everybody ends up reimplementing it
  • A function gets implemented if it gives non-trivial performance benefits over a reimplementation in Lua

Generally two of these should be true for a function to be added.

The reason why we implemented table.create is a combination of 1 and 3. You can not implement an efficient replacement for this function in some cases, and in some cases you can but the implementation is crazy (I think @Tomarty has one?)

The reason why we implemented table.find is a combination of 2 and 3. It’s often used so it makes sense to have it in the library, and it’s 2-3 times faster than Lua implementation.

table.map doesn’t really fit these right now:

  • It’s easy to implement in Lua
  • It’s not a commonly used mechanism in typical Roblox code (I recognize that some people are familiar with functional programming constructs, but the scope is just different vs table.find et al)
  • It’s not going to be faster if we implement it in C
  • Moreover, it is likely to be slower because every function call will go through C->Lua boundary
  • Every time you call this function, you’d have to allocate a closure for the transform function. So we would be inviting inefficient code.

Coincidentally we plan to implement some closure allocation optimizations that may make the last point a non-issue in the future, but the design hasn’t been finalized - as usual, there’s some odd interactions with getfenv/setfenv (aka my worst enemy).

So yeah, please use for loops for now.

5 Likes

As an aside to anyone who is interested, map is included in the rodash library, along with loads of other utilities to support functional programming constructs.

https://codekingdomsteam.github.io/rodash/api/Tables/#map

It’s not blazing fast, and shouldn’t be used in performance sensitive code, but it helps develop clean code which in most cases is what you’ll want to be writing.

1 Like

table.foreach is pretty much what you’re asking for. I believe it’s deprecated due to the stuff mentioned above.

@zeuxcg I’ll make sure to get my code to you sometime soon. It’s also a bit messy since it’s kind of a prototype of my algorithm so I may comment it for you if it helps out with testing.

1 Like

There are so many game-changing things in this post - thanks for investing the time into this.

“On Windows and Xbox, we’ve tuned our interpreter to be ~5-6% faster on Lua-intensive code”

Are there any changes to the performance of mobile devices? Particularly the lower-end devices.

1 Like

Most of the updates improve performance across the board; this one was highlighted in particular because it was specific to the compiler we use on Windows/Xbox builds. We didn’t do any additional mobile-specific tuning yet, although of course the Luau release itself improved the script execution performance noticeably on mobile devices as well (more so on Android than on iOS due to the hardware differences). I don’t think we have up-to-date numbers on this (performance got better), but here are numbers from April this year from an early unreleased version of Luau:

iOS, iPhone6
Lua time / Luau time 04/12
TerrainGenerator 1.61
N-Body 1.74
Life 1.74
Android, Pixel1
Lua time/Luau time 04/12
TerrainGenerator 1.64
N-Body 2.68
Life 3.12
Factorial 2.86
4 Likes