Timing of a delay timer

Godot Version

v4.1.1

Question

Hi folks,

I was trying to add some timing verification to a unit test and realized that when manually measuring a block that had a SceneTreeTimer delay in, the time always seems shorter than the specified delay. For example:

	var start := Time.get_ticks_msec()
	await get_tree().create_timer(4).timeout
	var end := Time.get_ticks_msec()
	print("Time delay for %s is %s" % [4, (end - start) / 1000.0])

typically will result in something like:

Time delay for 4 is 3.858

I realize that timings can be tricky, but I would think if anything it would have measured more not less than the delay time. Is there a better way manually time how long something takes to execute? Am I missing some else here?

Thanks.

Hello! I think you could try changing the process mode of the timer. The SceneTreeTimer syntax is:

SceneTreeTimer create_timer(time_sec: float, process_always: bool = true, process_in_physics: bool = false, ignore_time_scale: bool = false)

To make it fps independent, set process_in_physics to true like this:

await get_tree().create_timer(4, true, true).timeout

I haven’t tried it but it’s worth a shot.

1 Like

Thanks for the quick reply. I just tried it and had pretty much the same results. I must just be misunderstanding something here. Not a huge issue as this is just for timing in a unit test, but odd nonetheless.

Yeah I tried it as well and it didn’t give much of a difference. The best I got was 3.9s.

Update: I tried creating a helper function that times the process frame manually.

func wait(seconds: float) -> void:
	var t0 = Time.get_ticks_usec()
	while (Time.get_ticks_usec() - t0) < int(seconds * 1000000):
		await get_tree().process_frame

Surprisingly, it worked and was much accurate. I used it like this:

	var start = Time.get_ticks_usec()
	await wait(4.0)
	var end = Time.get_ticks_usec()
	print("Time delay for %s is %s" % [4, (end - start) / 1000000.0])

and the best it gave so far was this:

Time delay for 4 is 4.000816

Give it a try and see if it helps.

1 Like

Timers aren’t guaranteed to be accurate at less than half a second.

From the docs:

Note: Timers are affected by Engine.time_scale. The higher the time scale, the sooner timers will end. How often a timer processes may depend on the framerate or Engine.physics_ticks_per_second.

Also from the docs for create_timer:

If ignore_time_scale is true, the timer will ignore Engine.time_scale and update with the real, elapsed time.

So I’d try:

await get_tree().create_timer(4, true, false, true).timeout

It’s not Time.get_ticks_msec() that’s inaccurate, it’s the timer, and I don’t think trying to make it match the timer is not the way to solve the problem.

I tried this and it actually gave a much lower amount. Using this instead:

await get_tree().create_timer(4, true, true, true).timeout

makes it even worse, because it only waited for 0.6s. I think I need to study more about how Godot timings work.

1 Like

Thats not entirely true, most computers are not running under an atomic clock. There will always be some variation based on a multitude of factors.

This isnt a solvable problem in any meaningful sense. At some point the ‘time’ will need to be rounded to some value and that value always be ‘off’ to some point or another.

Yes, you are correct. To be more accurate my point was that Time.get_ticks_msec() returns the number of milliseconds since the game was started. A Timer only checks every process frame which is about every 16.666666666666668 milliseconds by default. Since they are running on the same processor, whatever time inaccuracies occur will be shared by both processes, and doesn’t really have a bearing on this conversation.

In unit testing, when you are testing the amount of time something takes, you typically test it within a tolerance range because of all the things we’ve been talking about. Personally, I would avoid any testing of await with a timer itself, as then it feels like you are testing the underlying Godot framework, not your own code. But, whatever floats one’s boat.

1 Like