Skip to content

PerformanceInliner: protect against misuse of @inline(__always) #64635

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

Merged
merged 2 commits into from
Mar 28, 2023

Conversation

eeckstein
Copy link
Contributor

Inline-always should only be used on relatively small functions. It must not be used on recursive functions.
Add a check that prevents that inlining of large @inline(__always) functions.

Also, add a command line option for better debugging large pass runtimes.

#64319
rdar://106655649

@eeckstein
Copy link
Contributor Author

@swift-ci test

@eeckstein
Copy link
Contributor Author

@swift-ci benchmark

Inline-always should only be used on relatively small functions. It must not be used on recursive functions.
Add a check that prevents that inlining of large @inline(__always) functions.

swiftlang#64319
rdar://106655649
@eeckstein
Copy link
Contributor Author

@swift-ci test

@eeckstein
Copy link
Contributor Author

@swift-ci benchmark

@karwa
Copy link
Contributor

karwa commented Mar 27, 2023

Is it possible for this to emit a diagnostic when @inline(__always) gets ignored?

@troughton
Copy link
Contributor

troughton commented Mar 28, 2023

This could potentially break one thing I've been using @inline(__always) for, which is to force constant folding of a function based on particular arguments. https://github.com/troughton/Substrate/blob/9eabdd212ae29b537609e846560c1f8123ce52cf/Sources/SubstrateImage/GaussianBlur.swift#L43 is an example of this – I want the body of _convolve to be copied into each of its callers, the pixelWeight and axis arguments to be constant folded, then _convolve to be deleted. Is there a way to support this use case while still disallowing recursive functions?

@eeckstein
Copy link
Contributor Author

eeckstein commented Mar 28, 2023

Is it possible for this to emit a diagnostic when @inline(__always) gets ignored?

It's complicated. Inlining decisions are made in the optimizer pipeline based on heuristics. Such a diagnostic would be very brittle. For example, you wouldn't see it when building in debug configuration. Unrelated code changes could let the diagnostic appear/disappear.

@inline(__always) is not a as strict as its name suggests. At the end it's only a hint for the optimizer which might be followed or (in some rare cases) ignored.

@eeckstein
Copy link
Contributor Author

eeckstein commented Mar 28, 2023

I want the body of _convolve to be copied into each of its callers, the pixelWeight and axis arguments to be constant folded, then _convolve to be deleted. Is there a way to support this use case while still disallowing recursive functions?

Is this for performance reasons? If yes, are you sure that the @inline(__always) makes a significant difference in performance?

There is a good reason that this attribute is underscored. The original intention of @inline(__always) was to annotate performance critical basic operations in the standard library.

There are only very few cases in "regular" programs (outside the stdlib) where @inline(__always) makes sense. Forcing inlining of a function doesn't necessarily improve performance and it's hard to predict the performance implications of @inline(__always) without considering the interaction with the inliner's default heuristic and other optimizations.

@troughton
Copy link
Contributor

troughton commented Mar 28, 2023

I want the body of _convolve to be copied into each of its callers, the pixelWeight and axis arguments to be constant folded, then _convolve to be deleted. Is there a way to support this use case while still disallowing recursive functions?

Is this for performance reasons? If yes, are you sure that the @inline(__always) makes a significant difference in performance?

It is for performance reasons, and I should have tested my assumptions beforehand. No, it doesn't seem to make any significant difference in performance (maybe a 2% margin between @inline(__always) and @inline(never)).

Manually doing the operation I hoped @inline(__always) would do (copying the function body, hardcoding the pixelSwizzle, removing the conditional checks, and inlining the pixelWeight closure) did result in a modest speedup for one function (~0.545s to ~0.505s) and a very significant speedup for the other (~0.923s to ~0.642s, both using XCTest's measure across multiple runs), so I guess there are other optimiser limitations at play here.

Being able to say "I want you to copy and specialise this function body for each of these constant inputs" would be a useful operation (since maintaining four near-identical copies of the same function isn't ideal), but obviously I was wrong to assume @inline(__always) would achieve that.

@eeckstein
Copy link
Contributor Author

The optimizer does specialize functions for certain kind of constants, including closures. But it's not configurable

@eeckstein eeckstein merged commit 85986f4 into swiftlang:main Mar 28, 2023
@eeckstein eeckstein deleted the fix-inline-always branch March 28, 2023 14:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants