Conversation
…Reference Fix a compile-time regression in Stream partitioning analysis (`partitionRegionConcurrencyReference`) when processing execution regions where many operations share a common resource operand (e.g., unrolled or sliced dispatches sharing an input tensor). In `partitionRegionConcurrencyReference`, for each operation and resource operand, all users of the operand were inspected. For each user op, the pass invoked `tiedOp.hasAnyTiedUses(operand)`. Because `hasAnyTiedUses` scanned linearly over all use sites of `operand` inside an outer loop over users, the check scaled cubically/quadratically with the number of dispatches sharing that resource: O(ops * users * uses). - Switch from `operand.getUsers()` to `operand.getUses()` in `ReferencePartitioning.cpp` to directly inspect each use site with its `OpOperand`. - Replace `hasAnyTiedUses(operand)` (O(uses)) with `isOperandTied(use.getOperandNumber())` (O(1)), eliminating the inner scan. - Add `seenTiedUsers` (`llvm::SmallPtrSet<Operation *, 4>`) to deduplicate hazard accumulation when an operation references the operand across multiple argument positions. Signed-off-by: isa-lai <isabellai1004@gmail.com>
|
Hello @isa-Lai 👋 Thank you for submitting a Pull Request to IREE! It looks like this is your first one. We have one ask, and you can also find some general tips below. Action required: acknowledge IREE project policiesIREE is a Linux Foundation project. All participants are expected to follow the LF Projects Code of Conduct. All contributions to IREE must follow our IREE AI Tool Use Policy. In particular:
We kindly ask you to reply to this message and confirm that you understand and accept the cited policies, particularly the AI Tool Use Policy. General guidanceOur general Contributing guide contains information and links to detailed guides on code quality, testing, commit summaries and our CI system. A common point for new PRs: if a DCO signing check fails for you, check out the section on Developer Certificate of Origin. If you have any questions, feel free to leave a comment here, or ask away on IREE Discord. Thank you, |
I understand and accept the cited policies. |
| llvm::SmallPtrSet<Operation *, 4> seenTiedUsers; | ||
| for (auto &use : operand.getUses()) { | ||
| Operation *user = use.getOwner(); | ||
| if (user == &op || user->getBlock() != block || | ||
| user->isBeforeInBlock(&op)) { | ||
| continue; | ||
| } | ||
| auto tiedOp = dyn_cast<IREE::Util::TiedOpInterface>(user); | ||
| if (!tiedOp || !tiedOp.hasAnyTiedUses(operand)) { | ||
| if (!tiedOp || !tiedOp.isOperandTied(use.getOperandNumber())) { | ||
| continue; | ||
| } | ||
| if (!seenTiedUsers.insert(user).second) { |
There was a problem hiding this comment.
isOperandTied changes semantics we now only check a single use instead of checking all uses of the operand globally.
However, given that hasAnyTiedUses is a static function there is no need at all to have this called in the function itself. We can check it before the loop and skip the entire user loop.
| llvm::SmallPtrSet<Operation *, 4> seenTiedUsers; | |
| for (auto &use : operand.getUses()) { | |
| Operation *user = use.getOwner(); | |
| if (user == &op || user->getBlock() != block || | |
| user->isBeforeInBlock(&op)) { | |
| continue; | |
| } | |
| auto tiedOp = dyn_cast<IREE::Util::TiedOpInterface>(user); | |
| if (!tiedOp || !tiedOp.hasAnyTiedUses(operand)) { | |
| if (!tiedOp || !tiedOp.isOperandTied(use.getOperandNumber())) { | |
| continue; | |
| } | |
| if (!seenTiedUsers.insert(user).second) { | |
| if (!IREE::Util::TiedOpInterface::hasAnyTiedUses(operand)) { | |
| continue; | |
| } | |
| llvm::SmallPtrSet<Operation *, 4> seenTiedUsers; | |
| for (auto &use : operand.getUses()) { | |
| Operation *user = use.getOwner(); | |
| if (user == &op || user->getBlock() != block || | |
| user->isBeforeInBlock(&op)) { | |
| continue; | |
| } | |
| if (!isa<IREE::Util::TiedOpInterface>(user)) { | |
| continue; | |
| } | |
| if (!seenTiedUsers.insert(user).second) { |
There was a problem hiding this comment.
The formatting got a bit messed up here, I hope it is clear what I am suggesting 😅
There was a problem hiding this comment.
Thank you for your submission! :)
I think we can improve even further by skipping the entire loop since the tied uses check is using a static function, no need at all to call this within the loop.
Also would you mind trimming down your PR description a bit it will be the commit message for this change :)
|
Hi @Manewing. Thanks for the review. But I think we still need to check Assuming we execute your suggested code on
In terms of the early stop using I also shorten the descriptor a bit. |
When analyzing execution regions where many operations share a common resource operand, partitionRegionConcurrencyReference previously iterated over operand.getUsers() and called tiedOp.hasAnyTiedUses(), causing a nested linear scan across all use sites.
Switch to iterating over operand.getUses() directly and check tiedOp.isOperandTied(use.getOperandNumber()) in O(1) time. Deduplicate accumulated operations using a SmallPtrSet so operands referenced across multiple slots on the same operation are only processed once.
Fixes #24928
Signed-off-by: @isa-Lai
Assisted-by: Gemini CLI