Msg.post() on Every Step?

Hi All,

I am writing a script that handles keyboard inputs (“w”,“a”, etc.) and then sends the resulting variable (in this case a number) to a url for that game object to handle. As part of this, the msg.post() is set to run on every step. I realised that it was simple enough to write something that would only msg.post() when the variable changed, which “felt” more “optimised”. Lacking the experience, I could not really justify this “feeling” other than ‘less messages to process’.

More broad, it is best practise to send a few messages as possible? Is it case dependant? Or does it actually not matter until I start sending millions of messages per second?

Any help would be greatly appreciated.

Unsurprisingly, it does depend :slight_smile:

Messages can get costly. Your case sounds fine. I suspect only sending it on change is more efficient, but I doubt that will end up being your bottle neck.

I recommend checking out the profiler - it includes time spent sending messages.

Edit: to be more specific, I think it will be a problem far earlier than millions per step. Think I got in trouble once I hit hundreds, but there was a lot of other stuff going on too.

3 Likes

If you need to send several messages every frame to communicate game state it is likely to become a bottleneck at some point as your game grows in complexity. Use messages to communicate state changes and game events. Use Lua modules to hold shared game state that can be accessed from your scripts.

4 Likes

The other aspect to consider is the cost of the on_message call in the receiving scripts. It’s pretty unlikely it’ll matter in your case, but I’ve seen it become a problem, with hundreds of physics callback messages every frame for example.

I always “pre-hash” my message ids so you’re not calling hash() every time for possibly every branch in your on_message function. This can save a significant amount of processing time.

local MY_MESSAGE = hash("my message")

function on_message(self, message_id, message, sender)
	if message_id == MY_MESSAGE then -- Instead of hash("my message") here.
		-- do stuff...
	end
end
5 Likes

I’ve always been curious if pre-hashing actually makes a difference, so I made a test project where a script sends x number of messages per update() to a script that either pre-hashes message ids, or doesn’t.

No hash script:

function on_message(self, message_id, message, sender)
	
	if message_id == hash("one") then
		-- print("one")
	elseif message_id == hash("two") then
		-- print("two")
	elseif message_id == hash("three") then
		-- print("three")
	elseif message_id == hash("four") then
		-- print("four")
	elseif message_id == hash("five") then
		-- print("five")
	end
end

Pre-hashed script:


local ONE = hash("one")
local TWO = hash("two")
local THREE = hash("three")
local FOUR = hash("four")
local FIVE = hash("five")

function on_message(self, message_id, message, sender)
	
	if message_id == ONE then
		-- print("one")
	elseif message_id == TWO then
		-- print("two")
	elseif message_id == THREE then
		-- print("three")
	elseif message_id == FOUR then
		-- print("four")
	elseif message_id == FIVE then
		-- print("five")
	end
end

Sending 1000 messages per update() meant the no hash script took, on my machine, 3-5 ms to process:

When sending the same number of messages to the pre-hashed script, it was more like 1 ms:

Yes, pre-hashing matters!

Because execution will stop when the correct message id is hit, sending the message “one” makes a huge difference vs sending the message “five” (default in this test). This further confirms that pre-hashing matters, as the more hashing you have to do, the longer it takes.

Project if you want to check it out for yourself:
(see follow up post)

8 Likes

Yep, check for the most frequent message ids first!

Although I’ve been thinking of doing a test where the message id is the key into a lookup table with message handlers. Something like this:

local ONE = hash("one")
local TWO = hash("two")
local THREE = hash("three")
local FOUR = hash("four")
local FIVE = hash("five")

function init(self)
	self.messages = {}
	self.messages[ONE] = function(self, message, sender) print("one") end
	self.messages[TWO] = function(self, message, sender) print("two") end
	self.messages[THREE] = function(self, message, sender) print("three") end
	self.messages[FOUR] = function(self, message, sender) print("four") end
	self.messages[FIVE] = function(self, message, sender) print("five") end
end

function on_message(self, message_id, message, sender)
	if self.messages[message_id] then
		self.messages[message_id](self, message, sender)
	end
end

I wonder if it’s faster or slower than the if-elseif-end? Probably slower due to the table lookup, but on the other hand it should be equally fast/slow for all messages.

5 Likes

I implemented your example and see similar results to the pre-hash. Note I’ve had to remove the print() everywhere because with thousands of messages that becomes the bottleneck rather than anything else.

Attaching the updated project - the old one had a mistake in it.

msg_hash_test.zip (6.6 KB)

Trying my best to eyeball the results, I get the below. You can ignore the actual numbers in my previous post as they were tainted by the mistake in my project.

Messaging “five”, 10k messages per update():
No hash: 2ms
Pre-hash: 0.75ms
Hash lookup: 0.5-1ms

Messaging “one”, 10k messages per update():
No hash: 0.5-1ms
Pre-hash: 0.2-0.3ms
Hash lookup: 0.6-1ms

Interestingly your theory seems to be correct - the lookup is consistent, and is slower than the first pre-hash but faster than the last one.

6 Likes

Thanks for this, nice and simple write up. Good to see some actual numbers behind the theory.

3 Likes

Thank you all for the great answers. It was very interesting to see the examples and the extra consideration of the pre-hashing. Looking at that profiler tool should help me in the future.

I know this is going a bit off topic, but I did want to bring something additional up:

This bit of advice has completely changes my whole approach to structuring my projects :sweat_smile:. I think it maybe ‘back to the manual’ for me, but (on the off chance) does anyone know of tutorials on structuring defold projects?

How do you structure your projects? There’s usually many different ways to structure your project and solve problems. For small games you can use almost any approach as even an inefficient approach is good enough. For large projects it usually is a good idea to use a combination of Lua modules, message passing and scripts.

1 Like

I have been trying out different structures and learning what works. The most successful one was using Lua modules as ‘states’ and creating state machines for game objects. This recent project was a result of using Nintendo Game Builder Garage and seeing if I could replicate the node sturcture using game objects.

My coding knowledge has gaps (like project structures and patterns), so I find it very useful when someone points in, or confirms, the ‘correct’ direction.

Yep, playing around with different solutions is a great way to learn what works well with the limitations set by the API of a game engine! And using Lua modules for state and shared data and functions is definitely a very good practice. Useful in both small and large projects!

1 Like