godotengine / godot

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

GdScript 2.0: Loading and expanding a script significantly slower than in Godot3 #67400

Open MikeSchulze opened 1 year ago

MikeSchulze commented 1 year ago

Godot version

v4.0.beta3.official [01ae26d31]

System information

Windows 10

Issue description

I need to load the original source code and extend it to use it in a kind of reflection. On Godot 3.5 the reload took around 8ms and now is significant increased to 165ms.

This is 20 times slower!

Steps to reproduce

build a small scene and add this kind of code

func _ready():
    var script := load("res://addons/gdUnit3/src/GdUnitTestSuite.gd")
    var start := OS.get_ticks_msec()
    build_custom_script(script)
    prints("load takes", Time.get_ticks_msec()-start)

func build_custom_script(source :GDScript ):
    var source_code :String = source.source_code
    source_code = source_code.replace("GdUnitTestSuite", "GdUnitTestSuiteX")
    source_code += """

func foo() :
    pass
    """ 
    var script := GDScript.new()
    script.source_code = source_code
    script.resource_path = "res://tmps.gd"
    script.reload()
Godot Version output
v3.5.stable.mono.official [991bb6ac7] load takes 8
v4.0.beta3.official [01ae26d31] load takes 165

Minimal reproduction project

No response

MikeSchulze commented 1 year ago

Is there any information as to why it loads so much slower?

Zireael07 commented 1 year ago

@MikeSchulze Have you checked with the rc? I know there was a recent slowdown related to spoofable unicode detection, and there were other fixes in the meantime...

MikeSchulze commented 1 year ago

still happend on RC1

vnen commented 1 year ago

Definitely requires some investigation and profiling, but the compiler in Godot 4.0 is much more thorough in checks, so it is expected that it is slower (though it shouldn't be that much slower).

This should be tested with ResourceLoader.load() and passing CACHE_MODE_IGNORE to make sure the resource cache is not being used.

Calinou commented 1 year ago

Minimal reproduction project (4.x): test_script_reloading_performance.zip

Reloading an empty script takes 0.1 ms on an i9-13900K on a release_debug build of 4.0.rc e930c8d38 (Linux, no LTO). Reloading the complex script (GdUnitTest4) takes 35 ms on the same CPU.

A release export template reloads the complex script in 24 ms.

Profiling a debug editor build (which takes 98 ms to reload the script) using HotSpot:

image

image

perf.data file for local inspection: perf.data.zip

MikeSchulze commented 1 year ago

Minimal reproduction project (4.x): [test_script_reloading_performance.zip]

A release export template reloads the complex script in 24 ms.

Profiling a debug editor build (which takes 98 ms to reload the script) using HotSpot:

Thank you for looking into this topic. If you have any tips for me to speed it up you are welcome

MikeSchulze commented 1 year ago

Hello, what is the current status on the subject?

MikeSchulze commented 1 year ago

Hi if there any news around this issue?

I am currently working on reducing the load time of my test suite class. What I see is that replacing the direct constructor with ResourceLoader.load() makes a big difference.

After replacing the constructors with ResourceLoader.load, the load time is reduced by about 50% from 24s to 12s across all test suites.

@Calinou from what I can see, probably when parsing the script, the classes are always reloaded not cached.

Example A:


func assert_foo() -> GdUnitAssert:
   return GdUnitStringAssert.new()

Example B:


func assert_foo() -> GdUnitAssert:
   return ResourceLoader.load("res://addons/gdUnit4/src/asserts/GdUnitStringAssertImpl.gd", "GDScript", ResourceLoader.CACHE_MODE_REUSE).new()

I guess on Godot3 the GdUnitStringAssert.new() was never parsed and on Godot4 it is now parsed?

MikeSchulze commented 1 year ago

If there any news about the loading performance? Please check by last comment.