Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add inline functions to GDScript #3760

Closed
Chaosus opened this issue Jan 5, 2022 · 7 comments
Closed

Add inline functions to GDScript #3760

Chaosus opened this issue Jan 5, 2022 · 7 comments

Comments

@Chaosus
Copy link
Member

Chaosus commented Jan 5, 2022

Describe the project you are working on

RTS-like games and prototypes

Describe the problem or limitation you are having in your project

It's a common issue in strategic games to get an index from x, y coordinates which for comfortability is placed in separate functions like:

func idx(x : int, y : int) -> int:
	return x + y * map_width

Then that function can be called in multiple places to get the index, for example:

class Tile:
	var height
	var unit

var data = []

func set_height(x : int, y : int, h : float) -> void:
	data[idx(x, y)].height = h
func set_unit(x : int, y : int, unit : Unit) -> void:
	data[idx(x, y)].unit = unit

I am afraid that this approach is not very optimal since idx may call very frequently and the performance would not optimal especially since GDScript is 20 times slower than C++.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

I suggest adding an inline keyword to GDScript to be used within a function declaration:

inline func idx(x : int, y : int):
	return x + y * map_width

Then it will receive an optimization that places the content of that function to the place where it's calling (similar to C/C++ define preprocessor command or compiler inline function declaration).

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Functions declared with inline may not be a subject of call method, ClassDB, or (maybe) documentation gathering.

I understand that it sounds like syntax sugar but it may be very useful to create compact and optimized code. I don't have much hope for this to be accepted but still decided to post this idea.

If this enhancement will not be used often, can it be worked around with a few lines of script?

no

Is there a reason why this should be core and not an add-on in the asset library?

maybe the GDScript compiler should make small functions inline automatically, but I don't know.

@Mickeon
Copy link

Mickeon commented Jan 5, 2022

I believe that optimisations under the hood like these should be handled by a smarter compiler, rather than being associated to keywords.
Compared to other keywords, inline would have little tangible effect to the average user, and its meaning can get confusing.

@me2beats
Copy link

me2beats commented Jan 5, 2022

what could inline keyword be confused with?

@me2beats
Copy link

me2beats commented Jan 5, 2022

I believe that optimisations under the hood like these should be handled by a smarter compiler

perhaps there may be cases when the compiler's work is undesirable in order to optimize such things. I don't know much about that, but I can assume that the "smartness" of the compiler can increase the compilation time of scripts (in the case of a large project),
whereas an inline keyword would tell the compiler which functions should be optimized, which would probably save compilation time. Again, this is just a guess.

@Calinou
Copy link
Member

Calinou commented Jan 5, 2022

I remember @vnen mentioning that he wanted to look at automatically inlining short functions and unrolling loops that have a known number of iterations at compile-time (until a certain limit is reached).

I would prefer going for this route rather than requiring users to manually add inline annotations everywhere. For comparison, C++ compilers have gotten pretty smart at inlining over the years, to the point that adding inline keywords in front of functions can actually decrease performance (or increase binary size with no performance benefit).

GDScript parsing/compilation times aren't a bottleneck1, and this step is skipped in projects exported in release mode anyway (thanks to .gdc bytecode files included in the PCK).

Footnotes

  1. What can be a bottleneck in large projects is the editor handling of scripts, but the script compiler itself is pretty fast.

@vnen
Copy link
Member

vnen commented Jan 7, 2022

Yeah, I'd rather make the compiler just optimize those cases instead of letting it to the user. Probably the main reason GDScript is slow is because of the lack of optimization steps. Optimization in general will be worked after 4.0 is released.

perhaps there may be cases when the compiler's work is undesirable in order to optimize such things. I don't know much about that, but I can assume that the "smartness" of the compiler can increase the compilation time of scripts (in the case of a large project),

Compilation time doesn't seem to be a problem now. If it becomes a problem because of optimization, then we can see what to do about that.

@Chaosus
Copy link
Member Author

Chaosus commented Jan 7, 2022

Okay, I will not argue with George :)

@just-like-that
Copy link

I support the idea of inlining (utility) functions:

  • if used the right way it makes the source code more readable
  • it brings some performance gains at no extra costs (see below)
  • and the build size might stay the same, too

I'm opting for an extra annotation called @inline.
This is meant as an compiler hint - compiler knows best what is going on under the hood.
No extra keyword - users who don't need it may ignore it / don't have to know it.

It comes in very handy especially for utility functions, which are one or two liners.
It makes code more readable and maintainable coz it's one single source of truth.

Utility functions like:

static func vector_is_positive(vector: Vector2) -> bool:
	return vector.abs() == vector

or

static func rect_center(rect: Rect2) -> Vector2:
	return rect.position + rect.size / 2

hide their simple or not so simple logic and have to be written only once.

At the same time it makes the code faster because the calling stack and such overhead is omitted.
At my own tests GDScript performed mediocre calling a utility function versus executing the same functionality directly in-place.
So performance-wise there is a gain without extra cost.

Incorporating the function directly might not even increase build size.

Ofc it's a good decision if the compiler has optimizations build in.

Lambdas might profit from this kind of annotation or compiler optimization strongly, too.
@vnen Do you have any plans on this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants