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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Inductor] Masked tl.load operations should explicitly include other if the masked out values are expected to be used #126535

Closed
alexbaden opened this issue May 17, 2024 · 3 comments
Labels
module: inductor oncall: pt2 triaged This issue has been looked at a team member, and triaged and prioritized into an appropriate module

Comments

@alexbaden
Copy link
Contributor

alexbaden commented May 17, 2024

馃悰 Describe the bug

The expected semantics of a Triton tl.load call with a mask supplied but no other parameter is to leave the masked out values (values where the mask is false) undefined. However, the CUDA backend in Triton explicitly zero-initializes masked out values as part of the predicated load instruction generated by the compiler. It appears that this behavior is being relied upon in Inductor (e.g. #126173) which results in undefined behavior from Inductor generated kernels on other Triton backends. In particular, the Intel backend used an undefined llvm value instead of 0-initialization when a masked load occurs w/out other.

The suggested solution is to add other where all masked loads are generated in Inductor, but looking at the code I noticed commentary around issues with masked loads when other is present (e.g.

). While the corresponding issue in Triton has been closed for some time, I am not sure what the side effects could be of adding other back to these areas. This issue is opened to investigate those.

In the meantime, we have decided to follow the CUDA backend implementation and explicitly zero initialize in the Intel XPU backend. It is possible this contains some performance benefit, in addition to fixing the bug above (though the performance benefit may just be a side effect of not trying to compare undefined values, I have not investigated). But, our reasoning is CUDA is the reference backend for Triton, and regardless of what the language spec says users are going to expect us to follow the CUDA backend semantics, particularly users who started with CUDA (like Inductor and PyTorch). It appears that the AMD Triton backend also zero-initializes, but I don't have hardware to test that.

I am happy to investigate adding other to the masked load operations (I found 3 or 4 of them doing a quick survey last night), but wanted to open this issue first for discussion.

Versions

main

cc @ezyang @msaroufim @bdhirsh @anijain2305 @chauhang @voznesenskym @penguinwu @EikanWang @jgong5 @Guobing-Chen @XiaobingSuper @zhuhaozhe @blzheng @wenzhe-nrv @jiayisunx @peterbell10 @ipiszy @yf225 @chenyang78 @kadeng @muchulee8 @ColinPeppler @amjames @desertfire

@ezyang
Copy link
Contributor

ezyang commented May 21, 2024

cc @jansel

@jansel
Copy link
Contributor

jansel commented May 21, 2024

Given that triton-lang/triton#2813 is fixed, I think it is fine to add other=... to inductor's generated code and remove that old workaround.

@alexbaden want to submit a PR to add that?

@alexbaden
Copy link
Contributor Author

Will do!

@xmfan xmfan added triaged This issue has been looked at a team member, and triaged and prioritized into an appropriate module and removed triage review labels May 21, 2024
pytorch-bot bot pushed a commit that referenced this issue May 30, 2024
For a masked `tl.load` operation, the Triton language specifies that values masked out (i.e. where the mask evaluates to false) are undefined in the output of the load. Triton provides an optional `other` parameter which, when included, provides an explicit value to use for masked out values from the load. If the output from a masked load without the `other` parameter is used in a conditional, unexpected behavior can occur.

Despite the language specification, all Triton backends currently in use by PyTorch Inductor (NVIDIA, AMD, and Intel) 0-initialize masked loads if `other` is not present (we recently changed the Intel backend behavior to match NVIDIA and AMD because that's what our users expect, even if we are not following the Triton spec to the tee). This PR attempts to "future-proof" Inductor for new backends (or perhaps changes in the current backends? - we did not see any performance change from 0-initializing in the Intel XPU backend but one could imagine compiler optimizations to remove paths that depend on undefined) to add an explicit `other` in instances where later conditionals depend on the `tl.load` output. I also removed an exception to `other` behavior for boolean loads, which was put in place for a Triton bug that should be fixed. I added `other` to the getting started documentation as a clue that masked load behavior requires explicit initialization if, even though I don't expect `undef` values to cause the example code to fail if the underlying output is not 0-initialized.  Finally, I added other to the `make_load` function in `select_algorithm.py`, though I wasn't able to determine if that function was actually being called.

Fixes #126535

Pull Request resolved: #127311
Approved by: https://github.com/jansel
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
module: inductor oncall: pt2 triaged This issue has been looked at a team member, and triaged and prioritized into an appropriate module
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants