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.

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}~~)",gameObject);~~

~~ }~~

~~ [UnityEditor.MenuItem("CONTEXT/InstatiationTester/ForEach “+nameof(InstatiationTester)+” "+nameof(SpawnPrefabs))]~~
~~ static void ForEach_InstatiationTester_SpawnPrefabs ()~~
~~ {~~
~~ foreach( var comp in FindObjectsOfType() )~~
~~ comp.SpawnPrefabs();~~
~~ }~~

}

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!