Wait and YieldFunctions do not produce lag
The wait command and anything that is considered a YieldFunction (ex. when using RemoteFunctions or DataStores) do not produce lag. They effectively pause the execution of the script until something has happened.
In Roblox, all scripts (and possibly even the physics engine) take turns executing. When a script says wait(5), it tells Roblox "I'm done executing for now. Wake me up in 5 seconds!"; Roblox then starts running the next script, until all scripts are done executing. Then it advances the game time (workspace.DistributedGameTime) and runs the physics.
What causes lag
"Lag" occurs when the server is trying to process more than it can handle and so cannot keep up with sending and receiving network updates to/from clients. This might occur if you have lots of bricks all colliding with each other, for instance (making Roblox do lots of physics calculations), or if you have lots of scripts doing complex or intensive work all at the same time.
Loops
Loops just ask Roblox to repeat a set of commands multiple times and are very cheap for Roblox to set up. However, if aren't careful to avoid repeating expensive operations, a loop makes it easy to get very poor performance. Let's take an example:
function Sum(startingSum)
local sum = startingSum or 0
for i = 1, 100 do
for j = 1, 10 do
for k = 1, 100 do
for m = 1, 10 do
sum = sum + i*j*k*m
end
end
end
end
return sum
end
print(Sum(0))
local result = {}
for start = 1, 10000 do
result[start] = Sum(start)
end
Sum() is an expensive operation. You see how the for loops are all inside each other (this is called "nesting")? When this happens, you multiply the number of times each loop will run together to get the total number of iterations (in this example, 100 * 10 * 100 * 10 = 1,000,000). Roblox can actually run Sum() on my computer in about 0.066 seconds (which means we could do this once every frame and not create any substantial lag). So, the print(Sum(0)) is no problem.
But then we take a look at the for start = 1, 10000 do loop. Since it calls Sum every iteration, we multiply the number of iterations together (10,000 * 1,000,000) to get 10 billion. We can see that this would cause prohibitive lag! (On my computer it would take approximately 11 minutes -- if my computer was acting as a Roblox server, this means that everyone's client would freeze for that entire time! My computer isn't nearly as powerful as Roblox servers, but even freezing for a minute is obviously no good).
Solutions to the scenario
There are two solutions to this scenario, assuming that we actually want/need the results.
- Add
wait commands to spread out the work
print(Sum(0))
local result = {}
for start = 1, 10000 do
result[start] = Sum(start)
wait()
end
The whole loop will not finish for many minutes, but the rest of the game can keep processing.
- Simplify the script. In this example, the script can be rewritten so that
Sum is only executed once. Though this won't be possible all the time in real life scripts, it's always good to do if you can. A better script (without changing Sum) would be this:
local baseSum = Sum()
print(baseSum)
local result = {}
for start = 1, 10000 do
result[start] = baseSum + start
end
This script will run very quickly.
The general lessons are these:
- If a function does a lot of work, try not to call it multiple times when you could cache the result (that is, save it to a variable) instead.
- Be wary of putting lots of loops inside each other if, when you multiply the number of times they will run together, you get a large number (ex one million). If it is too large, you either need to redesign, or consider using
wait commands so that you don't freeze Roblox.