Single loop for each object or one loop for all object

Hi devs! I’ve been trying to learn more about optimisation with loops but I’m kind of stuck with this problem I got in my mind. I did some searching on the forum but ended up with no luck.

-- Single loop for each object
for _, Part in pairs(PartFolder:GetChildren()) do
	game.RunService.Heartbeat:Connect(function()
		Part.CFrame *= CFrame.fromEulerAnglesXYZ(math.rad(1), 0, 0)
	end)
end

-- One loop for all object
game.RunService.Heartbeat:Connect(function()
	for _, Part in pairs(PartFolder:GetChildren()) do
		Part.CFrame *= CFrame.fromEulerAnglesXYZ(math.rad(1), 0, 0)
	end
end)

What I’m curious about is if I have around 500+ parts, which one would be more ideal in terms of optimisation and performance?

And would it help if I only make the part changes its CFrame if the said part is on the user’s screen?

Camera:WorldToScreenPoint(Position)

For your first question, a single event connection with one loop is probably faster.

For your second question, the cost of checking if an object is on screen will probably outweigh the cost of the calculation, so it would probably not be worth it.

Nobody can answer for certain though, the only way to really know is to measure.

1 Like

The first option is the right one, heartbeats should be as small as possible, but you need to store it in a variable and insert to a table so that you’re able to disconnect the events afterwards by looping through the table.

1 Like

I slightly disagree that heartbeats need to be as small as possible

The first method creates 500 events that will be executed every physics step.

The second method creates 1 event that does 500 things, which will be executed once every physics step.

The only real difference between the two is that the first one is more work for roblox, since it has to keep switching between all those events.

Either way I don’t think 500 CFrame transforms will hurt a game’s performance no matter how OP does it :slight_smile:

1 Like

The issue is that with so many parts, the second one wouldn’t run asynchronously and it would lag, unless you add a debounce which soft of defeats the purpose of a heartbeat.

I didn’t notice 500+, that’s really a wild scenario that should never happen. If it was just a few, a heartbeat for each is perfectly fine.

There will 100% be drawbacks no matter what option you choose if 500+

1 Like

Second one is better for you. In terms of performance these two will perform roughly the same so you want to take a deeper look at the technical implications of these methods. Optimisation between these two options won’t differ significantly.

Revisiting what I said earlier: why do I say the second one if they perform roughly the same then? The second one is going to consume less memory overall and the operation sample you gave is highly inexpensive to run, so might as well reduce your instances.

When you create connections a special instance called an RBXScriptConnection is made. While this connection is alive it’s going to be consuming some device memory. Now imagine you have a lot of these connections being created without being cleaned or anything… sounds bad, yeah? You additionally need memory registers for the functions because you’re using lambdas. So now you have a 2n memory consumption problem where every part will occupy a memory address for the function and some memory while the connection isn’t garbage collected.

In the case of the second option, you’re only creating one connection and one lambda, so your memory consumption is significantly lower now. You only create one RBXScriptConnection and occupy one memory address for the lambda. Less instances created, less memory consumed. Already that’s a plus point going towards the second option.

Now in general, the operation sample you gave is highly inexpensive to run. You might be designing for performance but be mindful of the engine’s capability to run code very fast. CFrame is one of, if not the, least expensive properties to update in the engine. Your for loop also doesn’t have any yielding code so they’re going to be executing nigh-instantaneously (at least to you as a developer).

So it all boils down to “500 connections vs 1 connection”. The for loop and the CFrame set are not expensive and multiplication is very fast as well so they’re not relevant to you figuring out which one may perform better since they’d evaluate in the same amount of time in both cases.

If you’re strongly curious about how this may perform for large quantities, consider opening debug profiles so you can inspect the MicroProfiler and see how long the tasks are taking.

debug.profilebegin("foobar")
-- Your code here
debug.profileend()
7 Likes