The Basics Of Basic Optimization

If someone made a plugin to automatically do the Raynew = Ray.new, etc in a script automatically I’d give them a big hug!

It’d need to add/remove the local RayNew = Ray.new / etc to the start dynamically based on whats used in the script

I could do this, but I need to factor in cases of someone having localization done already, because this would be redundant:

local Raynew = Ray.new
local ray = Raynew

Tip if anyone else does this type of plugin, check for things like " CFrame.new(" or “(CFrame.new(”, as someone could easily have “CustomTableCFrame.new()” register for “CFrame.new(”

1 Like

If they already had the localization for Ray.new then Ray.new wouldn’t be anywhere in the code and thus would not need the plugin to localize it

???
But the original code would have been this:

local ray = Ray.new

Oh right DERP,

Could check for = Ray.new or similar expressions, idk, this is why I’m counting on someone else doing the work for me :slight_smile:

1 Like

I have qualms about these kinds of “optimizations”.

The best way to make your work go faster is to do less work, not make each step 1% faster.


I recently was working on a graphical effect (the blue beam in this place). The gist of how it works is computing intersections between a box and all of the parts in the place.

It was dropping frames and still taking about 10 seconds to finish computing the animation (to only play back at about 5fps). I did lots of these “optimizations”:

  • I localized library functions like math.abs
  • I inlined functions and made local variables for constants like Vector3.new(.2, .2, .2)
  • I replaced tables with tuples of locals to avoid index operations
  • I pooled objects to avoid creating or changing too many

Guess what? These “optimizations” made zero impact on performance.

Why? Because the script does not spend any meaningful amount of time doing things like looking up variables and doing Vector3 constructions! If your script runs for 0.1 seconds, chances are the amount of time spent doing things like looking up keys in tables or globals is only a few milliseconds. Sure, a local variable lookup might be 100 times faster than a global variable lookup, but you’re eliminating 1ms from a 100ms script.

So what did I do? How do I optimize? It’s pretty obvious that “compute the intersection of this thin box with all of the parts” is wasting a lot of work.

Instead of using a list of all of the parts, I split it into something like an octree. Blam – I didn’t need to coerce all of my code into being less natural to write or read, I didn’t need to sacrifice any of the conveniences that Lua is supposed to give you.

It was instantly 100x faster – I can compute non-stop, in half the time, for an effect with 10 times as many frames.


TL;DR This is the wrong way to optimize. Do less work (using better data structures and algorithms). Don’t care about the speed of looking up your variable, because that’s not where you’re spending your time. Those things will only matter if your script is stuck running non-stop for seconds. If you’re doing that in a ROBLOX game, you have other problems.

These are things with smaller “complexity” in the language of CS.

Examples include:

  • linear search → binary search
  • linear search for best → heap
  • searching for nearest → quadtree / octree / dictionary
  • memoizing expensive (esp. recursive) functions
  • linked list ⇄ array (usually linked list → array)

local function this() end is valid syntax, you should probably use that if you only want to do a normal function definition but make it local.

60 Likes

BTW: Unless you’re really up for a challenge, don’t try to do heaps in Lua. Chances are you’ll have a really hard time getting the heap to be faster than a linear search for practical purposes. For example, every time I’ve implemented pathfinding on Roblox I’ve tried to implement heaps for the open-list and I’ve never gotten it to be faster than simpler solutions like bucketing the elements or even just linear search. If possible I’d recommend trying to keep a running max / min first if that’s an option (in the case of only adding or rarely removing elements) if you are considering a heap for something.

Also I’d like to add intrusive linked lists are awesome. That’s the seriously underused data structure in the Lua code I’ve seen on Roblox if anything.

4 Likes

Also learning some super basic computer science techniques can lead to coding practices that improve performance too. I’m not sure if I’d call it optimizations, but I guess changing the code to be more efficient could be an optimization.

For instance, if a certain behavior is different based on some static state, then longer code can be used to increase efficiency a bit:

local state = "idk" -- Constant; non-changing state
function DoSomething()
   if (state == "idk") then
      -- Foo
   else
      -- Bar
   end
end
local state = "idk"
local DoSomething
if (state == "idk") then
   DoSomething = function()
      -- Foo
   end
else
   DoSomething = function()
      -- Bar
   end
end

As you can see, the first snippet of code is shorter, but it forces a branch (the ‘if’ statement) every time it’s exectued. This is inefficient because we know that the condition being checked is a constant state and thus will be evaluated the same direction each time. In the second snippet, we write the function in two different ways based on the constant. Now the function will still perform differently based on the constant, but without any branching. (I believe some CPUs are smart enough to predict repetitive branch directions or whatever though, but that’s a whole other topic.)

12 Likes

That title could use some optimising.

24 Likes

If you’re for some reason doing the state check extremely often and you’re sure that’s causing performance issues, you’d be better of inlining the branch to avoid a function call entirely (unless you have very many states) and moving the state check “higher up” (thus having more duplicate code in each branch). Equality string comparisons are very fast in Lua.

Branch prediction (which really pretty much every processor has) probably has less of a positive influence on execution time of Lua scripts though, since those predictors cannot use the Lua-internal PC whatsoever.

There has been a reason I haven’t done a tutorial like this in a while; it always seems like I just fail at them and miss something massive.

I think it is a per case scenario. For my audio visualizer, localizing them made it go from 1.2%-1.8% in script performance to 0.1%-0.2%.

4 Likes

Wasn’t all of your data pre-calculated in that? That would probably mean that the slowest bit is just looking up data and setting properties; which would make sense that doing what you did made it faster.

For code which does lots of calculations, (e.g. inverse kinematics, physics simulation, etc.) BlueTaslem’s points make a lot more sense as they affect the speed much more.

1 Like

The data is, but the actual things like the UDim2s aren’t. About 64 are created per 1/60 of a second, so storing the index for UDim2.new does prevent a lot of those calls.

2 Likes

Generally speaking, you don’t want to optimize for things that aren’t a problem.
The usual workflow I’ve seen is to write code/features, run a profiler and work specifically on the problematic areas.

In roblox, the tools for this are somewhat lacking and poorly documented (something I’d like to fix in the near future), but the Micro Profiler will get you an idea of what’s taking the most time.

It’s usually not anything lua related.

5 Likes

Some tips I found online ( some don’t apply to ROBLOX ):

1 Like

Are these two ways of setting up a local function just as effective as each other?

local This = function()
---stuff
end
local function This()
---stuff
end

Both are defining local variables to be that function, so yes.

2 Likes

Okay that’s good, thanks for making this tutorial.

This times a bunch.

There is a difference.

local This = function()
	print(This)
end
local function This()
	print(This)
end
local This; This = function()
	print(This)
end

The first prints nil; this will not work as a recursive function. The second two print the function and can be used recursively.

10 Likes