Open loopier opened 1 month ago
It seems to be something on O(n^2), because tripling the number of actors to 1500 means the /rand
lines take around 10x longer to execute, compared to 500 actors. Little optimizations like moving initializations outside loops or trying to reduce object allocations don't seem to help.
The command arguments processing code is doing lots of repeated work, in particular when getting Actors by name. For example, in the /rand
command of your example, it finds all the matching Actors ("a*"), and calls the sub-command /x
for each of them. Lower, down, once the /property
command is called, each Actor (e.g. "a0", "a1", ...) is also "resolved" by calling getActors("a0")
, and so on...by string name (in this case, not a wildcard, but it could be. But we can't just replace the Actor name by the Actor object, because in "lower down" command/def calls, they all expect the Actor argument to be a string ("actor:s").
I think one way we could optimize this quite a bit would be to make Actor arguments have their own "type", maybe something like "actor:a". This special "type specification" would support either a string (name lookup with possible wildcards, same as currently), or else an Actor object, or even an array of Actors. If we already have an Actor or array of them, we just return that, without searching repeatedly (again) for actors in the scene hierarchy by string/name... This will give us quite a speed improvement, I'm pretty sure.
I don't have time to implement that tonight, but I was able to at least do some profiling and stepping into the code to understand what is slow. The main performance bottleneck is calling getActors()
500 times, in the above /rand
example with 500 Actors in the scene.
I added this test command, to check the performance for calling getActors()
500 times, and jus that alone takes basically the same time as calling your command: /rand /x a* 0 1920
(about 650ms on my machine)
# Add to the command map at the top of CommandInterface.gd:
"/perfTest": CommandDescription.new(perfTest, "", "Do some custom performance/profiling check"),
# And add this function:
func perfTest() -> Status:
var startTime := Time.get_ticks_usec()
for i in 500:
var result = getActors("a*")
var endTime := Time.get_ticks_usec()
print("Test end -- delta time %f ms" % [(endTime - startTime) * 1e-3])
return Status.ok(true)
When using
/rand
with large groups, the app pauses.This might be related to #51 and #52.
In the
evalCommand
func in Main.gd there are a fewif
statements that need to be resolved for each command. I'm starting to suspect that this is what is slowing everything down.