Support SVE with assembly implementation #762
No reviewers
Labels
No labels
announcement
bug
build
CI
dependencies
documentation
duplicate
enhancement
experience feedback
feature request
github_actions
help wanted
install
invalid
Long-term
optimization
portability
question
target-specific
test issue
wontfix
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gh-Cyan4973/xxHash!762
Loading…
Reference in a new issue
No description provided.
Delete branch "sve_03"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
With assembly implementation, performance on SVE could be improved continuously.
I presume we'll wait for the currently ongoing issues to be fixed (CI tests, notably for SVE mode, broken dispatch) before reviewing this PR.
Rebase the patch set since CI issue has been fixed.
This is a more complex PR, it will take some time to get through it.
To begin with, we may have another opportunity to divide and conquer here, and that would make each part easier to review. It seems there are 2 combined efforts that could be isolated :
SVE assembly : if I'm not mistaken, we already have an
SVEcode path inC. I presume the assembly version is probably more efficient. I also presume it neatly substitutes itself to the pureCcode path, without any other change of logic.This would make it possible to analyse this part on its own, separately.
The main issues will be around building and testing.
It's not yet clear if it can be merged into mainline, or is the territory of specialized forks.
arm dispatch : my understanding is that it extends the current
x86dispatch.It's unclear to me if this is a stand-alone capability that can be selected manually,
or if it's tied to some kind of automatic target determination system.
Here also, if it's possible to split that scope into smaller steps, this will increase the chances to pass review and get merged.
The main issues will be around complexity, both code and build complexities.
It has a decent chance to make it into mainline, but this outcome directly depends on the complexity introduced.
Speaking of Assembly version :
I was lucky enough to access a server with
SVEsupport, ac7gunit fromaws.Now, this SoC supports
SVE, but that's really all that can be said.Performance wasn't much different from
NEON, so I assume the vector unit is still limited to 128-bit.Nonetheless, this was a good opportunity to compare the
CSVEcode path with the Assembly one.And the difference was quite small, approximately +5% in favor of assembly.
Now, this could be because
c7gis not a greatSVEplatform.So a general question is : have you observed larger performance differences when benchmarking assembly
SVE? If yes, on which platforms and which scenarios ?This matters, because assembly introduces a substantial build difficulty, so it should be matched by some corresponding benefit. If +5% is about the right expectation, then mainline is probably not the best target for it (though it is still a good reason to create a specialized
SVEfork with this code).From a small amount of digging. c7g (AWS Graviton 3) seems to be based on the Neoverse V1, which is SVE-256.
Looking at the optimization guide:
ANDis better thanUXTWas it has more throughput and can be executed on both pipelines (whyyyy it is literally the same thing 😕)XXH3_NEON_LANES=8is probably optimal on this processor since it has 4 NEON decoders.However, I question how much of the C performance is just compilers not being quite ready yet and in a few months the entire code will be obsolete (especially since now there are SVE compatible machines that are more readily available now).
So your guess is that
SVEperformance on Graviton3 could have been better,but it's a matter of correctly optimizing for this architecture (manually or via compiler).
Yes. If Graviton3 is an ARM design and not designed in-house, it is likely just as sensitive to instruction ordering and pipelining as the Cortexes I've been fiddling with.
As for the interleaved SVE, try replacing the sve256 loop block (L294 to L306) with this. Disclaimer, I haven't tested this and my ordering might not be ideal.
Unfortunately, my access to this platform was very temporary.
I don't have it anymore...
As a side note I wonder if it is beneficial to just use NEON on SVE-128.
No biggie.
Let me check whether I can get aws c7g.
I mainly worked on Fujitsu A64FX platform. The performance is improved a lot with assembly code. As I remember, A64FX is based on ARMv8.2, and AWS c7g maybe based on ARMv8.4.
Yes, I mainly verified it on Fujitsu A64FX.
By the way I did some research on the A64FX, and from what it appears, NEON has been severely performance-deprecated (as in everything but the trivial instructions having 6-12 cycles latency) to put more power into SVE. This is an understandable design choice because it is designed for specialized uses and not general purpose.
So NEON being bad on this particular platform is unavoidable.
I tried it on c7g.

SVE doesn't work as expected. I'll keep investigating. As my understanding, SVE should work a bit better than NEON.
@easyaspi314 There's some issue in your code snippet. "LSR <Zdn>.<T>, <Pg>/M, <Zdn>.<T>, <Zm>.<T>". If I switch to AND instruction from UXTW, I need one more instruction. So I could not gain benefit.
I think that for now we should only do SVE-512.
Looking at the optimization guide, c7g is a tradeoff because while SVE can process 2x the data, NEON always has at least 2x the IPC. (Also NEON already has 256-bit loads).
Also scalar can still be executed in the background, and we lose that benefit unless we mix in NEON which would just be messy and not worth the complexity.
However, with 100% optimal code, SVE will lose because the multiply is significantly slower and can't be parallelized:
umlalmad(Parentheses are if the result can't be forwarded to another add instruction, which isn't the case in XXH3)
The ARM pipeline is so fun 💀
SVE2 may have better performance because it can use
umlalbinstead ofand/uxtw+mad, but currently all SVE2-compatible processors are 128-bit, and the only reason SVE is viable is bandwidth.Yes, I agree on it. SVE don't improve a lot performance on SVE-128 & SVE-256.
On SVE-256 (V1 core), I tried to tune assembly code. The latest performance is attached.

But it also drops while buffer size is increased. It's very similar as what happened on A64FX. I'll continue to check whether it could be improved.
I can simply list what I did.
The debug code is in branch debug_sve_03 of my repo.
I found something interesting on different chips.
We know that NEON works well on Neoverse N1 & V1 core. But I also find that NEON works worse than scalar on Fujitsu A64FX and Hisilicon Kirin920. These two chips are older than N1 & V1.
My point is that vector instruction may not bring great performance in initial. But the cores are always evolving. They may become better in the future.
Good point. Let me check whether it could bing benefits.
I assume that SVE is more sensitive to cache.
Will take a look, thanks.
There are some issues with 32-bit ARM that I want to work out which would cause issues on the latter. A lot of it seems to be issues with load-store.
If I write XXH3_accumulate_512_neon in 32-bit ARM inline asm, I can get a significant performance boost of up to 12 GB/s vs 7 GB/s. However, this is at a tradeoff of practicality — Google and Apple require all apps to have 64-bit support, so this will only really benefit Android devices that are at least 6 years old and armv7l Linux. I want the main target to be AArch64 and only go for low-footprint optimizations for 32-bit ARM.
Hopefully. It unfortunately seems like ARM has gone back to 128-bit SVE for its newest designs, including the V2. This might be because, as we see on c7g, the throughput penalty makes it not that much better than 2 NEON instructions.
c7g does not have SVE2.
However, if you have access to an SVE2 machine with a known 128-bit vector size, try replacing
XXH3_accumulate_512_neonwith this. This is just NEON but usesrev64+umlalbfor a significantly faster shuffle+multiply. It requires manually setting the vector length though.Also make sure to put
#include <arm_neon.h>above the line that includes<arm_sve.h>.When compiling, use these flags (also try both GCC and Clang)
@easyaspi314 Although both NEON and SVE2 support the multiplication from two 32-bit inputs to 64-bit result. They're totally different.
Yes, and the reason it is favorable is that instead of requiring the
uzp1/uzp2setup, it can be done withrev64. The complicated shuffle is what makes NEON less efficient than SSE2.Sorry, I didn't get the point.
After
uzp1/uzp2instruction, the data layout is changed in below.After
umullbinstruction, the data layout is changed in below. For convenience, I don't useumlalbat here.But we hope
D3L*D3Hshould be stayed in 0x10, not 0x08. Then I need to more instructions to make them in order.rev64can't help them in order at here.Ah, you are confused because the uzp trick is for two vectors at once. This is for only one.
Come to think of it this would actually have literally zero benefit over the two vector approach aside from a minor data dependency for things that are going to be executed 4 at a time with identical timings (on the performance cores)
Specifically, instead of
uzp1+uzp2for 2 vectors, it isrev64for one, and those both are 2 cycles, 4 throughput.I take back what I said, SVE-128 is garbage, just use NEON 🤪
I tried to mix NEON and SVE2. At first, compiler reported error while I convert NEON data type to SVE type. Then, I switched to assembly. I met some strange error. While declaring
.arch armv8-a+sve2in assembly, compiler reported sve architecture is required. I don't know what's wrong with it. I failed it both in QEMU and SVE2 hardware that is provided by Ali cloud.I plan to simplify the patch set. In dispatch, it checks the cpu. If it's SVE512, turn to assembly routine. If it's not, turn to NEON routine. Could it be acceptable?
It's fixed.
Since it's assembly, I only mix NEON and SVE2. I'll mix Scalar, NEON & SVE2 later.
With the help of SVE2, it could save one instruction and gain the performance. (RED vs GREEN)
That difference might solely be from it being handwritten assembly. However, even if it wasn't, I'd say that even if it is interleaved with scalar it clearly isn't going to do much to be worth an entirely different target from NEON.
I would say that the minimum to warrant something like that would be 20%, and it should be on a newer target that will be worth even more in the future and not an older one (hence why I wouldn't want an entire ARMv7-A NEON inline assembly implementation, even though it benefits performance)
OK. How about SVE512 assembly code? Could it be accepted?
I'd say yes, although I would recommend the following priority:
Also, dispatching on POSIX ELF would be trivial:
Agreed,
this first stage looks likely uncontroversial.
We haven't looked at this PR in a while,
I'm trying to get back to it during this Summer
in an attempt to determine which parts can be merged for next release.
I believe it's fair to say that our current code base is not ready to add an assembly file at this point. Merging
xxh_arm64sve.Sis probably not going to happen this cycle.Note that, In the future, we'll try to change the code base to support multiple files, which might open a door for assembly source code again, but that's for later.
As for now, I note that this PR was also introducing other interesting tools and capability.
xxh_arm64dispatch.cfor example, which is an equivalent ofxxh_x86dispatch.cbut foraarch64targets, looks interesting.More refined arch detection in
xsum_arch.halso looks interesting.There might be more.
I was wondering which parts of this PR could be salvaged for
v0.8.2release, and went for the simpler part, which is aSVEdetection prompt for the CLI.Another interesting technology presented in this PR is the
arm64automatic vector dispatch, which is a nice counterpoint to thex86/x64one.However, there is a non-negligible difference :
on
x64cpus, there is an obvious improvement path, fromSSE2toAVX2and thenAVX512. Each iteration is strictly better than the previous one.For
aarch64, it's not so clear .SVEmight be better thanNEON, but it may also not be.If my understanding of previous discussions is correct,
SVEis expected to beatNEONwhen it can operate, in hardware, with 512-bit vector lengths (or better). But at 128-bit, it's most likely less efficient thanNEON. And 256-bit is not clear-cut either.This means, the hardware detection is not limited to "just" detecting
SVEsupport or not, but also what's the hardware width of vector units. My understanding is that the currently proposed dispatcher is not able to do that, and it's unclear if detecting this width at runtime is doable.Anyway, this will require a bit more work in order to be merged.
At this point, I mostly wonder if this PR should remain opened, for reference, with the idea that a future
aarch64dispatcher could be created from it, or if it should be closed.Closing, due to lack of activity.
While the topic of providing
XXH3/XXH128usingSVEis interesting,it still has to overcome a few obstacles,
the most serious of which are:
SVEis not an automatic win overNEON, and is more likely to be a regression on mostarmchipsets, save a few specific ones with very wide vector registers. That makes it a very specific option, that should only be enabled on explicit request.Pull request closed