-
-
Notifications
You must be signed in to change notification settings - Fork 654
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
Needless allocation for the last of multiple identical operations #10765
Comments
Seems like somehow the last expression in a block is treated differently. Two things work around this:
|
I hope the Optimizer could be a little more perfect, until '-D analyzer-optimize' is enabled by default. |
This is governed by the I'm not sure if it's safe to always start off with this being |
@Simn yes, I think this is correct. The |
Hi, it seems that this issue still happens in Haxe 4.3.2 when the code is in a loop: https://try.haxe.org/#91D89228. public inline function copyFrom(other: ComplexArray) {
// No allocation here
abstract[0] = other[0];
for (i in 0...this.byteLength >>> 3) {
// Also no allocation
abstract[0] = other[0];
// ... but here there is
abstract[0] = other[0];
}
// No allocation here
abstract[0] = other[0];
} JS Output: let imag = arrayB.getFloat32(4);
arrayA.setFloat32(0,arrayB.getFloat32(0));
arrayA.setFloat32(4,imag);
let _g = 0;
let _g1 = arrayA.byteLength >>> 3;
while(_g < _g1) {
++_g;
let imag = arrayB.getFloat32(4);
arrayA.setFloat32(0,arrayB.getFloat32(0));
arrayA.setFloat32(4,imag);
let value = new Complex(arrayB.getFloat32(0),arrayB.getFloat32(4)); // <--
arrayA.setFloat32(0,value.real)
arrayA.setFloat32(4,value.imag);
}
let imag1 = arrayB.getFloat32(4);
arrayA.setFloat32(0,arrayB.getFloat32(0));
arrayA.setFloat32(4,imag1); @Simn Please let me know if I should open a new issue for this :) Edit: Another observation: The same issue also happens at the end of if/else blocks when they're inside a loop: https://try.haxe.org/#8020BA9e. The noop workaround mentioned in #10765 (comment) needs to be included in the if/else blocks then in order to work. |
The reason why inlining is being cancelled in this example is because the last statment of the while block is a reference to a potentially inlined object. The constructor inliner logic doesn't have a special case for the body of while statements and the default behaviour of the inliner is to cancel the inlining if it sees a reference escape into expressions for which it doesn't have a special case. @MoritzBrueckner if you want to work around this limitation you can just make the last expression in the while block be something else, for example: for (i in 0...this.byteLength >>> 3) {
// Also no allocation
abstract[0] = other[0];
// ... but here there is
abstract[0] = other[0];
null; // This is a noop but it will make the constructor inliner know that the previous expression is not leaking the reference.
} |
Thanks, just to be sure that I correctly understand the reason: it is because of the implicit "return" (or however you might call that in this case) of the last expression of |
It's 100% possible to improve the constructor inliner so that these cases are handled without breaking any valid code. To be more clear about why it's failing: The constructor inliner doesn't understand while expressions and so when it sees while(..condition...) referenceToPotentiallyInlinedObject; It treats it in the same way as: someNonInlinedFunction(referenceToPotentiallyInlinedObject); Basically, any time it finds an expression in a situation that it doesn't have a special case for, it will cancel the inlining of any object the expression references. |
Hi,
the following example https://try.haxe.org/#5eFC48BC contains three identical lines of code
In the generated JS code, the last of those lines (independent of the number of identical lines!) gets an allocation where it isn't required. All but the last occurrences of this line get transpiled to
whereas the last one instead becomes
for some reason. It doesn't really make sense here to allocate memory just for a temporary variable, and it's slow. If you remove one original line or duplicate it once more, it is still only the last occurrence that results in an allocation.
If you force inlining of all functions in the example by making them
extern
, the last of the identical lines in Haxe produces the errorIt's pretty frustrating because the original code (where the lines aren't completely identical, but very similar) is quite a performance bottleneck. Because the used
ComplexArray
does not containComplex
objects, I need to usenew Complex()
in theget()
method if I want to have a complex number type instead of doing all the float inlining myself all the time.Apart from that issue, I wish there was information in the error message that tells you why the inlining is not possible, so that one can try to find a workaround. Otherwise it's pretty useless in some situations.
Thanks :)
Full example code, in case the link to it gets invalid some day (click to expand)
This produces (JS):
The text was updated successfully, but these errors were encountered: