- Home /
Instantiate() is taking up to 300ms after creating a large number of objects in edit mode
I have a script attached to a terrain that procedurally generates a forest that runs exponentially slowly as the area generated gets larger, from a reasonable 10 seconds total for 300 objects to an outlandish 10 minutes for 1700 objects. Putting the Instantiate call behind a simple stopwatch-print statement shows that it starts out at ~1ms for the first object and then climbs fairly consistently to 300ms. Probably irrelevant context: all of these objects use the same prefab (a 1500-vertex mesh) and they all need to have the same parent.
Why does Instantiate seem to run in On^3 or worse compared to the number of objects already instantiated? Is this an internal memory/gc issue? Is there a way to get Unity to manually flush the sort of object registry that could be causing this? Finally, is there a way to get the inbuilt profiler to work in edit mode?
Things I've tried: switching to new GameObject(), which seemed to have the exact same issue.
Edit: After more prying it is clear that the cause is the specific set of positions I'm giving the Instantiate call.
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
GameObject hex = Instantiate(hexPrefab, hexContainer);
stopwatch.Stop();
print("Instantiate took " + stopwatch.Elapsed);
stopwatch.Reset();
stopwatch.Start();
hex.transform.position = location;
stopwatch.Stop();
print("Moving and rotating took " + stopwatch.Elapsed);
In this code the Instantiate call itself continues getting slower after every call. Bizarrely, if the position change after the Instantiate call is commented out, then the Instantiate call continues at constant speed. I haven't been able to reproduce this outside my own script yet. I will pry more in the next few days, but my only thoughts are that this could be caused by some sort of collision check or very consistent multithreading operation behind the scenes.
Based on your edit, what if you try setting the position using the Instantiate(Object original, Vector3 position, Quaternion rotation, Transform parent) variant instead?
That is how I had it originally and it makes no or very little difference to execution time compared to breaking it apart.
Answer by GamerGear · Jul 07, 2021 at 11:07 PM
Solved... mostly. For reasons that are beyond my determination, Instantiate()'s execution time depends heavily and nonlinearly on the general stack height in which it's called. At a stack height of 3000 (very high but not unreasonable) it slows all the way down to 1.5s, a 1500x impairment for me. At times this was dependent on changing the position of the object but at others it made no difference. If anyone has any more insight I would still love to hear it.
Moral of the story: DON'T USE INSTANTIATE UNDER RECURSION!
Answer by andrew-lukasik · Jul 07, 2021 at 08:46 AM
Does this script reproduce your results? Because I see linear growth, nothing extra.
using UnityEngine;
using Stopwatch = System.Diagnostics.Stopwatch;
public class InstatiationTester : MonoBehaviour
{
[SerializeField] GameObject _prefab;
[SerializeField] int _count = 1;
void SpawnPrefabs ()
{
var sw = Stopwatch.StartNew();
var parent = new GameObject($"batch of {_count} x \"{_prefab.name}\" ({System.DateTime.Now.TimeOfDay.ToString()})").transform;
UnityEditor.Undo.RegisterCreatedObjectUndo( parent.gameObject , "SpawnPrefabs" );
for( int i=0 ; i<_count ; i++ )
{
var pos = Vector3.Scale( Random.insideUnitSphere , new Vector3{ x=100f , z=100f } );
var instance = Instantiate( _prefab , pos , Random.rotation , parent );
}
Debug.Log($"Instantiate( {_prefab.name} ) * {_count} took {sw.ElapsedMilliseconds} [ms] ({sw.ElapsedMilliseconds/1000d:0.00}[s])",gameObject);
}
[UnityEditor.MenuItem("CONTEXT/InstatiationTester/ForEach "+nameof(InstatiationTester)+" "+nameof(SpawnPrefabs))]
static void ForEach_InstatiationTester_SpawnPrefabs ()
{
foreach( var comp in FindObjectsOfType<InstatiationTester>() )
comp.SpawnPrefabs();
}
}
It does not! But that only makes things more confusing. I picked slightly more and, bizarrely, it's definitely the specific locations I'm generating that are causing the issue. I'll edit the question accordingly. It looks like I'll have to take a big crowbar to this in the next few days and try to find a pattern in these positions but I'm not too confident.
I place my bet on Undo system being behind it somehow. Because this thing can be costly; I've seen situations where ctrl+z meant system restart because everything hang and it took so long.
Your answer
Follow this Question
Related Questions
Continually Updating position of Instantiated Object (to Mouse Pointer) 0 Answers
Spawning GameObject contained within another 1 Answer
getting .Transform from instanntiated object 2 Answers
How to make sure only one GameObject is instantiated at a time. 3 Answers
problems parenting a gameObject to another upon instantiating when same name exists 1 Answer