godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.46k stars 21.07k forks source link

Many properties in _get_property_list() slows down instantiate() by 2-8x. #97829

Open chairfull opened 2 weeks ago

chairfull commented 2 weeks ago

Tested versions

System information

Godot v4.3.stable - Ubuntu 24.04.1 LTS 24.04 - X11 - GLES3 (Compatibility) - AMD Radeon R9 200 Series (radeonsi, pitcairn, LLVM 17.0.6, DRM 3.57, 6.8.0-45-generic) - AMD FX(tm)-6300 Six-Core Processor (6 Threads)

Issue description

When a script has a _get_property_list() method with many properties, then calling load().instantiate() is much slower (2-8x in my case) while testing from the editor.

But when the project is built, the lag is gone. So it's only in editor.

I feel the obvious solution is below, but it doesn't work.

func _get_property_list():
    if not Engine.is_editor_hint():
        return []
    # Properties here...

Steps to reproduce

var a := 0.0 var b := "b" var c := [1, 2, 3, 4] var d := false var e := { x=false } var f := Vector2.ZERO

var bool_a := false var bool_b := true var bool_c := true var bool_d := false var bool_e := false var bool_f := true

var str_a := "a" var str_b := "bb" var str_c := "ccc" var str_d := "dddd" var str_e := "eeeee" var str_f := "ffffff"

var arr_a := [] var arr_b := [1] var arr_c := [false, true] var arr_d := [[], [], [], []] var arr_e := [{}, {}, {}, {}, {}] var arr_f := ["f", "ff", "fff", "ffff", "fffff"]

var dict_a := {} var dict_b := {x=1} var dict_c := {a=1, b=2} var dict_d := {a=true, b=true, c=true} var dict_e := {x=[], y=false, c=true, e=Vector2.ZERO} var dict_f := {a="a", b="b", c="c", d="d", e="e"}

var np_a := ^"../property_list" var np_b := ^"../property_list" var np_c := ^"../property_list" var np_d := ^"../property_list" var np_e := ^"../property_list" var np_f := ^"../property_list"

func _get_property_list() -> Array[Dictionary]: var props: Array[Dictionary]

if not Engine.is_editor_hint():
    return props

props.append({ name="a", type=TYPE_FLOAT })
props.append({ name="b", type=TYPE_STRING })
props.append({ name="c", type=TYPE_ARRAY })
props.append({ name="e", type=TYPE_DICTIONARY })
props.append({ name="f", type=TYPE_VECTOR2 })

props.append({ name="Bool", type=TYPE_NIL, usage=PROPERTY_USAGE_GROUP, hint_string="bool_" })
props.append({ name="bool_a", type=TYPE_BOOL })
props.append({ name="bool_b", type=TYPE_BOOL })
props.append({ name="bool_c", type=TYPE_BOOL })
props.append({ name="bool_d", type=TYPE_BOOL })
props.append({ name="bool_e", type=TYPE_BOOL })
props.append({ name="bool_f", type=TYPE_BOOL })

props.append({ name="String", type=TYPE_NIL, usage=PROPERTY_USAGE_GROUP, hint_string="str_" })
props.append({ name="str_a", type=TYPE_STRING })
props.append({ name="str_b", type=TYPE_STRING })
props.append({ name="str_c", type=TYPE_STRING })
props.append({ name="str_d", type=TYPE_STRING })
props.append({ name="str_e", type=TYPE_STRING })
props.append({ name="str_f", type=TYPE_STRING })

props.append({ name="Array", type=TYPE_NIL, usage=PROPERTY_USAGE_GROUP, hint_string="arr_" })
props.append({ name="arr_a", type=TYPE_ARRAY })
props.append({ name="arr_b", type=TYPE_ARRAY })
props.append({ name="arr_c", type=TYPE_ARRAY })
props.append({ name="arr_d", type=TYPE_ARRAY })
props.append({ name="arr_e", type=TYPE_ARRAY })
props.append({ name="arr_f", type=TYPE_ARRAY })

props.append({ name="Dict", type=TYPE_NIL, usage=PROPERTY_USAGE_GROUP, hint_string="dict_" })
props.append({ name="dict_a", type=TYPE_DICTIONARY })
props.append({ name="dict_b", type=TYPE_DICTIONARY })
props.append({ name="dict_c", type=TYPE_DICTIONARY })
props.append({ name="dict_d", type=TYPE_DICTIONARY })
props.append({ name="dict_e", type=TYPE_DICTIONARY })
props.append({ name="dict_f", type=TYPE_DICTIONARY })

props.append({ name="NodePath", type=TYPE_NIL, usage=PROPERTY_USAGE_GROUP, hint_string="np_" })
props.append({ name="np_a", type=TYPE_NODE_PATH })
props.append({ name="np_b", type=TYPE_NODE_PATH })
props.append({ name="np_c", type=TYPE_NODE_PATH })
props.append({ name="np_d", type=TYPE_NODE_PATH })
props.append({ name="np_e", type=TYPE_NODE_PATH })
props.append({ name="np_f", type=TYPE_NODE_PATH })

return props

- From another scene, run this script.
```gdscript
func _ready():
    var t1 := Time.get_ticks_msec()
    for i in 1000:
        var _node: Node = load("res://test_scene.tscn").instantiate()
    var tt1 := Time.get_ticks_msec() - t1
    prints("non: ", tt1)

Minimal reproduction project (MRP)

test_property_list.zip

huwpascoe commented 2 weeks ago
Test results for 4.3 without <-- Engine.is_editor_hint() --> with non prp dif non prp dif
696 790 1.13 689 663 0.96
660 783 1.18 662 654 0.98
656 780 1.18 659 652 0.98
654 780 1.19 661 657 0.99
aiaimimi0920 commented 2 weeks ago

@huwpascoe

Test results for 4.4 dev 3 without <-- Engine.is_editor_hint() --> with

non prp dif   non prp dif
174 284 1.63   171 260 1.52
177 266 1.50   164 255 1.55
164 259 1.58   162 237 1.46
152 263 1.73   160 255 1.59

this is my system: Godot v4.4.dev3 - Windows 10.0.22631 - Multi-window, 1 monitor - OpenGL 3 (Compatibility) - NVIDIA GeForce RTX 2080 Ti (NVIDIA; 32.0.15.6094) - 12th Gen Intel(R) Core(TM) i5-12400F (12 threads)