Performance difference between running stuff in and out of a function

I noticed that there’s a difference between running stuff in separate functions and running stuff in one function. To see if this was actually true I made a little script to see if there’s a difference and if so to see what the difference is.

I ran math.sqrt(1e6) in three different loops under heartbeat. One without any functions one in a function and one in a function in a module script.

This is that script:

local Module = require(script.ModuleScript)

local function Function()
	math.sqrt(1e6)
end

game:GetService("RunService").Heartbeat:Connect(function()
	debug.profilebegin("Out Function") -- Math in the same function
	for i = 1e5, 0, -1 do
		math.sqrt(1e6)
	end
	debug.profileend()
	
	debug.profilebegin("In Function") -- Math in another function
	for i = 1e5, 0, -1 do
		Function()
	end
	debug.profileend()
	
	debug.profilebegin("In Module Function") -- Math in a function in a module script
	for i = 1e5, 0, -1 do
		Module.Function()
	end
	debug.profileend()
end)

The result was interesting though:

image image image

There was actually a huge difference between them, with the one that’s out function being almost 2x as fast as the one that didn’t call the function every time. Although it fires the function 100.000 times, a 2 to 1 ratio is unbelievable.

And to confirm it isn’t just isn’t just one frame I used tick() to see what the average is over 5 seconds:

image

Using functions is clearly slower than not using them, but functions are like the main way to structure your scripts and they also prevent writing the same code multiple times.

So how do you write performant scripts without them turning into a hot mess?

For code that’s used repeatedly every frame I tend to manually inline the code; i.e. replace the call site with whatever operation(s) I’m trying to perform.

Inlining does often come at the expensive of readability though so it’s dependent on what I’m trying to achieve and how likely / how much computation time I’ll save.

See here for some answers to the Module.Function() call.

Note that the following sections here, here and here may also be of interest to you if you’re taking a deeper look at microoptimisations - some of these optimisations might explain the somewhat surprisingly significant difference seen in your first two examples.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.