This is an archive of the discontinued LLVM Phabricator instance.

[SLP]Improve costs of vectorized loads/stores by analyzing GEPs.
ClosedPublic

Authored by ABataev on Oct 5 2022, 9:29 AM.

Details

Summary

When generating masked gathers nodes, SLP vectorizer accounts the cost
of the GEPs for loads as part of the scalar-vector transformation cost
estimation. But it does not do it for vectorized loads/stores, while it
may completely remove some of the GEPs completely. Because of this in
some cases masked gather operation can be much more profitable rather
than regular vectorization (masked-gather cost + vector GEP - scalar
loads + GEPs comparing to vectorized loads - scalar loads).
Added the analysis of the removed scalarGEPs for vectorized load/store nodes for better cost estimation.

Diff Detail

Event Timeline

ABataev created this revision.Oct 5 2022, 9:29 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 5 2022, 9:29 AM
ABataev requested review of this revision.Oct 5 2022, 9:29 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 5 2022, 9:29 AM
RKSimon added inline comments.Oct 12 2022, 4:32 AM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6654

Ptr->hasAllConstantIndices() ?

RKSimon added inline comments.Oct 12 2022, 4:35 AM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6646

Better to use OperandValueInfo() instead of the initialization?

ABataev updated this revision to Diff 467122.Oct 12 2022, 6:44 AM

Address comments

RKSimon added inline comments.Oct 12 2022, 6:58 AM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6691

hasAllConstantIndices

11545

hasAllConstantIndices

ABataev updated this revision to Diff 467147.Oct 12 2022, 7:46 AM

Address comments

RKSimon accepted this revision.Oct 13 2022, 1:29 AM

LGTM - in the medium term I think we should be trying to move more of this into getGEPCost - but that callback needs improvement first as its barely used.

This revision is now accepted and ready to land.Oct 13 2022, 1:29 AM
vdmitrie added inline comments.Nov 17 2022, 6:31 PM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6656

We see quite a significant performance regression related to this patch.
It does not look the right adjustment. For x86 specifically these GEPS cost nothing as they end up merely as different displacement values in memory operands. So the bias towards vectorization isn't justified for plain loads and stores.
It can be seen even for test case test/Transforms/SLPVectorizer/X86/remark_not_all_parts.ll
Vectorization makes code less profitable here. It already existed before the patch but this patch but cost modeling although tipped over to vectorization it was close enough to say "not profitable".
But now we have even more bias.

Vecorized code:
Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)

[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.25 subq $136, %rsp
1 0 0.17 xorl %ecx, %ecx
1 0 0.17 xorl %eax, %eax
1 5 0.50 * movq (%rdi,%rcx), %xmm0
1 5 0.50 * movq 16(%rdi,%rcx), %xmm1
1 1 0.33 paddd %xmm0, %xmm1
1 2 1.00 movd %xmm1, %edx
1 1 0.25 addl %eax, %edx
2 1 1.00 * movq %xmm1, -128(%rsp,%rcx)
1 1 1.00 pshufd $85, %xmm1, %xmm0
1 2 1.00 movd %xmm0, %eax
1 1 0.25 addl %edx, %eax
1 1 0.25 addq $32, %rcx
1 1 0.25 cmpq $256, %rcx
1 1 0.50 jne .LBB0_1
1 1 0.25 addq $136, %rsp
3 7 1.00 U retq

Original:

[1] [2] [3] [4] [5] [6] Instructions:
1 1 0.25 subq $136, %rsp
1 0 0.17 xorl %eax, %eax
1 1 0.25 movq $-256, %rcx
1 5 0.50 * movl 272(%rdi,%rcx), %edx
2 6 0.50 * addl 256(%rdi,%rcx), %edx
1 1 1.00 * movl %edx, 128(%rsp,%rcx)
1 5 0.50 * movl 276(%rdi,%rcx), %esi
2 6 0.50 * addl 260(%rdi,%rcx), %esi
1 1 1.00 * movl %esi, 132(%rsp,%rcx)
1 1 0.25 addl %esi, %eax
1 1 0.25 addl %edx, %eax
1 1 0.25 addq $32, %rcx
1 1 0.50 jne .LBB0_1
1 1 0.25 addq $136, %rsp
3 7 1.00 U retq

ABataev added inline comments.Nov 18 2022, 3:25 AM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6656

I can say, it was expected. That's why there was a discussion about using getGEPCost instead of this. This changes just syncs cost estimation for masked gathers and vector loads. As you noted, we already had the issue with the geps costs. We need to fix this. It would help if Intel will try to implement their part of getGEPCost and we can start using it here for better cost estimation.

vdmitrie added inline comments.Nov 18 2022, 9:39 AM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6656

You got me wrong. I did not say we already had issues with geps. What I did I did say is: we already had issues with vectorizing sequences when we should not. Most issues with these wrongful vectorizations come from shuffles and permutations generated. And the example, which is the test case in this patch (remark_not_all_parts.ll) merely does show that vectorized code is worse that the original.
And I don't think that you anticipated 60% performance regression from this patch. But that is what we have now. I suggest you to revert this change for these reasons:

  • huge regression it introduced. It makes bad things even worse. Cost of inserts and permutations on integer vectors seems underestimated. That is where most regressions come from. But adding unjustified bias to the cost towards vectorization makes the problem even worse.
  • the CM heuristics added does not reflect real thigs - it goes into displacement part of a memory operand which costs nothing.
  • test cases which changed within this patch do not show where the patch would help. Moreover they create impression that nothing has changed. But that isn't the case. Can you show any real test case which shows how this patch improves vectorization?

Aligning gather loads does not justify enough IMO. May be gathers have this same issue?
Can you point at a test case for gather loads?

ABataev added inline comments.Nov 18 2022, 9:47 AM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6656

It is not to improve the vectorization but to fix the cost difference between vector loads and masked gather. If we're going to revert it, we need to remove the geps cost estimation for masked gathers. Otherwise there are cases, where consecutive loads are less profitable than the masked gather, and it leads to the perf regressions.

vdmitrie added inline comments.Nov 18 2022, 10:04 AM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6656

Yes, we better to focus on these cases and find out why gather loads look more profitable (instead of making unit stride load look less profitable). It can be because of the same issue with geps or it can be that gather load cost itself is too optimistic.
For gather loads basically indices are populated at run time - so this per-element ADD cost should go into gather load rather than scalar loads. Although it most likely will be a single load + ADD of displacements stored in constants pool.

ABataev added inline comments.Nov 18 2022, 10:11 AM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6656

The reason is know - need to fix the cost estimation for GEPs. And we need to fix getGEPCost function. Without this we are overoptimistic about GEPs. And we need to fix the cost in getGEPCost function and use it in the cost estimation. This will fix the regression introduced in this patch and fix general problem.
Reverting the patch won't fix the issue, it will just hide it.

vdmitrie added inline comments.Nov 18 2022, 10:22 AM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6656

Could you point out the test case (where GEPS are incorrectly estimated) please?
So far general problem that I see is that scalar load cost too overestimated and that adds too much favor towards vectorization.

ABataev added inline comments.Nov 18 2022, 10:37 AM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6656

I don't remember exactly currently, found out when worked on strided loads vectorization. We ignored the cost of the GEPs for vector loads and because of that vector loads became less profitable than the masked gather with vectorized GEPs (the strided load cost is higher than the vector load, but vectorized GEPs currently are more profitable than the scalar ones, since the cost of each GEP is currently calculated as the cost of ADD).

So far general problem that I see is that scalar load cost too overestimated and that adds too much favor towards vectorization.

Yes, right. And we need to fix couple things about it - the cost for GEPs (which are free for many cases on X86) and the cost of scalar/vector loads (which also are free in many cases for X86). But I just don't have enough time to do it. I would appreciate it if you could try implement the GEP cost for X86 and we could switch to getGEPCost instead of using the cost of simple ADD for GEPs.

vdmitrie added inline comments.Nov 18 2022, 2:32 PM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6656

To be honest, I still do not understand what is the problem with GEPs. GEPs only have cost when stride is unknown. But if we end up with "vectorize" state node here we are already ensured that stride is known and and it is unit stride(i.e. we load or store adjacent elements in memory). That equally applies to loads and stores but the thing is here in SLP we don't yet issue scatter stores yet (if I've not missed something). So what problem did you suppose to solve when applied this GEP adjustment to stores is still unclear. It's unfortunate that you lost the test case that reasoned you for this patch. Any chance to recover it somehow?

ABataev added inline comments.Nov 18 2022, 2:37 PM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6656

To be honest, I still do not understand what is the problem with GEPs.

Different cost model for GEPS for masked loads and vector loads.

GEPs only have cost when stride is unknown.

For X86, but there other archs.

But if we end up with "vectorize" state node here we are already ensured that stride is known and and it is unit stride(i.e. we load or store adjacent elements in memory). That equally applies to loads and stores but the thing is here in SLP we don't yet issue scatter stores yet (if I've not missed something). So what problem did you suppose to solve when applied this GEP adjustment to stores is still unclear. It's unfortunate that you lost the test case that reasoned you for this patch. Any chance to recover it somehow?

I did not lost it, just need some time to find it.

vdmitrie added inline comments.Nov 18 2022, 5:25 PM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6656

Different cost model for GEPS for masked loads and vector loads.

Different does not mean incorrect.

For X86, but there other archs.

Thanks for admitting this was inappropriate place to fix problem. It should be a part of target dependent TTI implementation for the target that you meant to fix. Now it equally applies to all targets.

ABataev added inline comments.Nov 18 2022, 5:42 PM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6656

Different cost model for GEPS for masked loads and vector loads.

Different does not mean incorrect.

In this case it is incorrect.

For X86, but there other archs.

Thanks for admitting this was inappropriate place to fix problem. It should be a part of target dependent TTI implementation for the target that you meant to fix. Now it equally applies to all targets.

That's why I asked you to help with the implementation of getGEPCost in TTI for X86 so we could use it here instead of add cost.

vdmitrie added inline comments.Nov 21 2022, 11:28 AM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6656

I'm willing to work on it. But I cannot begin earlier than after couple of weeks (before Dec 6th to be precise). And we need to meet somewhere to discuss, share ideas and sync on this issue (phab is not the right place for that). It would be nice if you could find test cases that can be reduced (if not already) to show case the issue to address.

ABataev added inline comments.Nov 22 2022, 6:40 AM
llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
6656

Sounds good. We can discuss it via e-mail or set up a meeting.