- Home /
Managing a lot of GameObjects, calling functions on 1000 objects in a loop is very slow - Optimize/ECS/Delegates?
Hi, I basically spawn a 1000ish objects which have a script and either a line or mesh renderer on them. I later try to loop through them to enable those mesh/line renderers. The reason is for creating an effect of seeing them all drawn one by one rather than having them all appear at the same time. However it seems its very slow to access all of these GameObjects and run a function on their scripts. It's in a coroutine because I want to be able to see them appear one by one, not all at once. The cost of the objects, the rendering, and the actual code being accessed does not seem to be a lot - its just that iterating through this list and calling a function on each GameObject seems slow - takes about 10-15 seconds, and this does not reduce a lot if I disable what the functions are actually doing.
Is there an obvious solution I am missing here? If I want to trigger something on a 1000 GameObjects at once, would it better to use something like events or delegates? Is this a use case where ECS would be applicable, and result in a boost?
Thanks
IEnumerator AnimateNeurons()
{
if (!isRendered)
{
isTiming = true;
for(int i=0; i<somas.Length; i++)
{
somas[i].EnableRenderer();
//segments = neuron.transform.GetComponentsInChildren<SegmentAnimation>();
//Debug.Log("Number of segments - " + segments.Length);
//foreach (SegmentAnimation seg in segments)
//{
// seg.EnableRenderer();
// yield return null;
//}
yield return null;
}
isRendered = true;
}
Debug.Log("Finished coroutine, took " + timer);
yield return null;
}
There are 302 somas, the function I call just enables individual mesh renderers. There's an inner loop I commented out which enables line renderers on children of the somas - which total to around a 1000. Its commented out because I tried doing that inside the soma scripts instead of in this loop, to see if it helped. But even iterating through 302 somas takes 10-15 seconds. I realize I might be using coroutines incorrectly, so that might be the issue.
You need to show us the scripts. In most case your script might not functioning like the way you think
I've updated the question with some code and additional explanation. I realize I might be using coroutines incorrectly, which might be causing the issue.
Please show the script, especially the loop part. Calling a function 1000 times should not be that performance intensive. Do you call GetComponent()
a lot on the loop? Have you profiled what takes long?
I've updated the question with some code and additional explanation. I realize I might be using coroutines incorrectly, which might be causing the issue.
I haven't profiled but what I tried is removing the actual content of the function, so its just a loop calling an empty function. That didn't seem to improve the speed at all. Which is why I feel its an issue either with how I'm assu$$anonymous$$g coroutines work or with just looping through too many objects.
Thanks for posting the code and adding additional explanation.
The code is running "slow" not because of being sub-optimal, but because after enabling each soma you are telling it to wait for the next frame (due to yield return null;
). For ~300 objects at 60 FPS it will take 5 seconds for all of the objects to enable.
But isn't this what you wanted to achieve, make them appear one by one, ins$$anonymous$$d of all of them together? Or you just don't want all of them to appear together, but it is fine of more than one appears? In that case, decide over how much time or how many frames you want the objects to appear, and enable total number / duration
objects each frame.
Some code:
[SerializeField] private int numFramesToAppear = 15; // about 0.25 seconds @ 60 FPS
private IEnumerator AnimateNeurons () {
isTi$$anonymous$$g = true;
int numToEnableEachFrame = somas.Length / numFramesToAppear; // I might be off by 1 here, so that not all objects will get enabled; in that case add +1 to numFramesToAppear
int frameCounter = 0;
while (frameCounter < numFramesToAppear) {
int startIndex = frameCounter * numToEnableEachFrame;
for (int i = startIndex; i < startIndex + numToEnableEachFrame && i < somas.Length; ++i) { somas[i].EnableRenderer(); }
frameCounter++;
yield return null;
}
Debug.Log("Finished coroutine, took " + timer);
}
Answer by NoDumbQuestion · Jun 13, 2018 at 08:42 AM
The issue is quite simple.
yield return null;
return null mean wait for next update(). You are waiting every update to Enable render.
That's a good point, I'll try changing that. $$anonymous$$y assumption was that yield return null would be the fastest way to do this. If ins$$anonymous$$d i had yield return new WaitForSeconds(0.1f), or a smaller value, would that actually be faster and still be non-blocking? Since it's not limited by how often update is being called?
It won't make any different.
Don't use couroutine. Use time countdown. How many spawn per seconds?
Call update. Check time delta from last frame. Spawn that many soma based on your spawn value. Then enable them in update. When all enable. Disable that time countdown object.
couroutine will be called in next update() anyway so no need to call wait. The result will be the same
You're right, that didn't work. I will try what you've suggested now. I had considered that, but I thought it was better practice/cleaner code to do it in a coroutine. I was thinking of a coroutine as sort of a non-blocking function. Like an extension of update. Is there no way to achieve what I want to do using coroutines? I'm just curious. Thanks
Your answer

Follow this Question
Related Questions
Speeding up nested for loop of structs and calculations 0 Answers
How do I optimize the "Player Loop"? 2 Answers
Need tips for optimizing main func that loops through thousands of particles 0 Answers
How to play many animations at once without slowdown? 3 Answers
Looping through vector3 list slow 1 Answer