Should I split a huge function into smaller ones?

Godot Version

any

Question

Is it a good practice to split a huge function into many smaller sub-functions? For example, in a player script, I have _physics_process(delta) function which takes 231 lines of code. Does it take much performance / memory when splitting the function into 9 subfunctions like this:

func _physics_process(delta:float) -> void:
  _check_input()
  _check_collisions()
  _update_sprite()
  ...
# Subfunctions:
func _check_input() -> void:
  ...
func _check_collisions() -> void:
  ...
func _update_sprite() -> void:
  ...
...

Absolutely. It makes the code a lot more readable.

I used this code to test that:

extends Node2D

func _ready() -> void:
	var time_sum: int = 0
	for i in range(100):
		var start_time: int = Time.get_ticks_msec()
	
		for j in range(1000):
			printraw("1")
		printraw("\n")
		for j in range(1000):
			printraw("2")
		printraw("\n")
		for j in range(1000):
			printraw("3")
		printraw("\n")
		for j in range(1000):
			printraw("4")
		printraw("\n")
		for j in range(1000):
			printraw("5")
		printraw("\n")
		
		time_sum += Time.get_ticks_msec() - start_time

	print("Average time elapsed without functions: %f" % (float(time_sum) / 100 / 1000))

	var function_time_sum: int = 0
	for i in range(100):
		var function_start_time: int = Time.get_ticks_msec()
		
		_1()
		_2()
		_3()
		_4()
		_5()
		
		function_time_sum += Time.get_ticks_msec() - function_start_time
		
	print("Average time elapsed with functions: %f" % (float(function_time_sum) / 100 / 1000))

func _1() -> void:
	for i in range(1000):
		printraw("1")
	printraw("\n")


func _2() -> void:
	for i in range(1000):
		printraw("2")
	printraw("\n")


func _3() -> void:
	for i in range(1000):
		printraw("3")
	printraw("\n")


func _4() -> void:
	for i in range(1000):
		printraw("4")
	printraw("\n")


func _5() -> void:
	for i in range(1000):
		printraw("5")
	printraw("\n")

And there was no consistent difference whatsoever. One of the two methods tended to be around a millisecond faster than the other but that could very well just be a precision error.

So yes, it is good practice to split up functions and no, it doesn’t take much performance. I can’t say anything about the memory but I think it should only add around a hundred bytes at most

2 Likes

Your test code could be bottle-necked by the amount of printraw calls you can make, if I’m not mistaken.

Technically speaking creating function calls is more expensive than just looping over arrays, because one grows the function stack, the other does not.

Then again, it even matters how you loop over nested arrays. Technically speaking arr[i][j] is not the same speed with arr[j][i].

I did not find any Godot resources on this, but the principles should carry over from other programming languages, such as Python.

Not as a rule. In many cases it just fragments your code and makes it harder to follow the flow as you need to jump around the source code. Size shouldn’t be the primary criterion for extracting a function. Extract when you see same or similar pieces of code in more than 2 places.

Also don’t worry about the performance cost. Doing a call will always be a bit more costly than not doing it but optimize only wen profiling data show that it is a significant bottleneck. An example may be having many function calls in a loop body with many iterations, running every frame. Even that needn’t be optimized if it doesn’t choke the framerate.

3 Likes

I find that considering splitting a large function into smaller chunks can also make me think harder about the variables, their scope and purpose, how best to pass values between the subfunctions and are those subfunctions useful anywhere else. Occasionally a perfectly acceptable answer was to leave it in a big function, but at least I’ve made the effort to validate my approach.

1 Like

Thank you guys :heart: