Ah, I suppose.
2,245 karma · joined January 31, 2021
Ah, I suppose.
Yeah, fusing is probably easier, if you already know what to fuse. On the other hand, if you want to fuse load pair on RISC-V you have the entire rename stage to figure out which uops can be fused independently of the rename stage, if fusion haopens after rename as well.
No, every high performance core I know of cracks them at decode, some re-fuse some of them after rename (Apple). Because otherwise you would need to rename up to 4 destinations per rename slot, effectively 4xing your already limiting rename stage.
Cracking other stuff later in the pipeline isn't expensive.
Take for example a post increment load, which does a=mem[b++], notice how this writes to two registers. Handeling two writes (up to 4) would explode the stage after decode (rename). So high performance arm implementations generate two uops for this. But since the number of decoders is fixed and the number of rename slots as well, you now have alnost the same problem as in RISC-V with compressed instructions: the nth input to the rename stage can come from a variaty of outputs of the decode stage, so you need a large shuffle network, and propagate the uop counts from start to end.
Cracking is a lot cheaper, if you can do it later in the pipeline. E.g. the cheapest is if you can simply "replay" the instruction. That is, instead of removing the entry from the issue queue, when it starts executing, you decrement a counter and keep the entry to do something else next. But as I mentioned that doesn't really work with multiple write back.
Do you have any specific project in mind that I could use for testing?
Armv9-a doesn't mandate FP or SIMD support, but nobody does detection for those, why? Because it's required on the OS level. Similarly OS are moving their baseline to RVA23 so software can assume all of those instructions are available.
It is, with a prefix encoding, you can reuse the RVC decode path 1-to-1 and get the 48/64-bit instruction starts with a simple bitshift (or simply handle the 48/64-bit instructions via the fusion path). This seems to be the encoding direction RISC-V is headed in.
Arm uses complex instructions with multiple writeback, that require cracking, to improve code density. RISC-V uses a variable length encoding to improve code density. Both have anaougus decoding complexity, but RISC-V achieves higher code density, while impacting the cost of things before decode (how much, idk).
RISC-V is not an ISA, but an ISA generation framework.
If RISC-V would've standardized aarch64 1-to-1, the end result would've still been a huge extension mess, because a lot of people (RVI member) have different requirements and a very happy to build their own subsets, which would then be upstreamed because multiple vendors want the same subsets and compatibility between them. Obviously it would've been better, similar to if RISC-V spawned with RVA23 done, but development takes time and RISC-V International started, because people where already using RISC-V.
RISC-V also is the most DOSed ISA, with people proposing crazy stuff. Just the other day somebody proposed an instruction that would do up to 2^30 16-bit comparisons in one instruction at the largest VLEN. Because they wanted to improve their string processing usecase.
---
In my experience RVA23 matches aarch64 and x86 in uop count (without fusion), code density is better, instruction count is slightly higher. The biggest impact on the instruction count advantage of aarch64 over RVA23 is a single instruction, load-pair, which gets cracked at decode in every high-performance implementation, because it writes to to registers.
The Arm approach to code density is using multiple writeback instructions that have to be cracked and the RISC-V one is RVC. Both prohibit simple linear scaling of parallel decoding, so code density seems to have mattered to Arm enough to make the tradeoff worth it.
RISC-V is not an ISA, but an ISA generation framework.
If RISC-V would've standardized aarch64 1-to-1, the end result would've still been a huge extension mess, because a lot of people (RVI member) have different requirements and a very happy to build their own subsets, which would then be upstreamed because multiple vendors want the same subsets and compatibility between them. Obviously it would've been better, similar to if RISC-V spawned with RVA23 done, but development takes time and RISC-V International started, because people where already using RISC-V.
RISC-V also is the most DOSed ISA, with people proposing crazy stuff. Just the other day somebody proposed an instruction that would do up to 2^30 16-bit comparisons in one instruction at the largest VLEN. Because they wanted to improve their string processing usecase.
---
In my experience RVA23 matches aarch64 and x86 in uop count (without fusion), code density is better, instruction count is slightly higher. The biggest impact on the instruction count advantage of aarch64 over RVA23 is a single instruction, load-pair, which gets cracked at decode in every high-performance implementation, because it writes to to registers.
The Arm approach to code density is using multiple writeback instructions that have to be cracked and the RISC-V one is RVC. Both prohibit simple linear scaling of parallel decoding, so code density seems to have mattered to Arm enough to make the tradeoff worth it.
btw, how much protein do you consider recommended?
I often see these vegan meat substitute products as an on-ramp and people who are vegan for longer tend to use other things in their staple dishes.
Yes, this is what I was saying, but twice the vector width of AVX-512 will perform horrible in SSE, which is why portable SIMD abstractions should make writing code relative to the native vector width simple.
> I pass in the vector width as a generic parameter like this:
> fn do_simd_stuff<const N: usize>(x: Simd<f32, N>) { ... }
My problem is that no portable_simd example code I've seen does this, which causes people to choose one specific N and run with that.
The second part of the problem is how you find the native vector length, so you can instantiate the generic function. IIRC this isn't even exposed in portable_simd and you have to use a seperate crate to get it.
If you use f32x16 (the avx-512 wisth), SSE now effectively has 4 registers to work with and will spill when doing anything beyond the most simple stuff.
The default should imo be relative to the native register width, so you can do 1x, 2x or sometimes 4x the native width, depensing on your register preasure.
There is no reason a portable_simd relu_dot implemention should need to specify the SIMD width.
But the design and documentation of portable_simd makes the fixed size syntactically easy/the default and the width agnostic code harder.
They specifies a constant SIMD width so it's non-portable. Well, not performance portable, but why are we using SIMD again?
Actually, why not just have a single generic 1-bit flag?
You could still have cmp + branch, but now you put the comparison type into the cmp. This gives you a larger branch range again. A flag setting add/sub could also simply set that flag on carry. cmov works, some form of ccmp also works.
Well, yes, because GPUs need 3r1w anyways, for fmadd, and because a dedicated cmov (blen/merge) is a lot more important in those workloads. RVV also has "cmov" vmerge and full predication in general.
RISC-V wants to avoid needing to 3r1w on the integer side.
> But it does require three input registers. One for a control value, plus the two options. And RISC-V has a thing about not requiring three input registers.... Yeah, I'm not a fan of RISC-V.
Arm also tried to avoid 3R1W on the scalar integer side. It's really only madd (on one or two ALUs) and some of the complex load/store ops, which are usually cracked.
My guess is that arm has 3R1W integer instructions, because they needed load/store pair for code density. That means they have to support renaming 3R1W integer instructions, so you may as well add integer madd. I don't think they would've added 3r1w if it wasn't for the complex load/stores.
> One of the other major advantages of flags is it allowed for a cheap third input to certain instructions (like cmov, adc, ccmp), without requiring a full 3R1W register file.
Exactlty. Although I don't think the 3R1W instructions increase the register file size that much, because it grows linear with read ports and you usually don't need the full read bandwidth on very wide cores, as you can do port stealing. I think it's mostly about rename.
> But MIPS already proved it was possible, and showed all the downsides, it did not need to be tried a second time with RISC-V.
I think it's a fine way to design an ISA, however Zicond should've been on the base ISA.
But I hadn't considered the ISA design option of only carry flag, no seperate cmp and branch, before.
And yet, flag writing instructions are usually half the throughput on regular ALU instructions, on modern wide ooo designs. [1, 2]
But I generally agree, there is a good way of handling them, it's hust unclear to me how expensive it is. It can't be that expensive, but apparently it is expensive enough, to not put a mask register write port on all integer execution units.
[1] https://dougallj.github.io/applecpu/firestorm-int.html (see how ADD is 6-issue and ADDS 3-issue)
[2] https://developer.arm.com/documentation/111027/4-0/ (again 8-issue ADD, but 4-issue ADDS)
On the one hand, this will be quite straight forward, but on the other hand quite disappointing.
Afaik Dart has a 128-bit only SIMD abstraction (so not performance portable by default). Since the base "V" extension mandates a mininum vector length of 128-bit, you can trivially make codegen work for all vector length, by simply setting vl to 128/elementwidth.
But as with x86, if your native hardware vector length is larger than 128-bit, you leave performance on the table.
> This is mostly because their approach to SIMD is so different, but also because I can't test it at all. Are there any RISC-V "machines"?
I'd recommend using qemu for initial testing.
Hardware wise, the cheapest option is the orange pi rv2, which has 8 SpacemiT X60 cores, which are in-order and support 256-bit RVV. The Zhihe A210 is also interesting, but way to expensive for what it is.
If you have a higher budget, I'd recommend the SpacemiT K3, which is the fist RISC-V SBC with RVA23 support. It is has 8 SpacemiT X100 4-wide out-of-order cores, with 256-bit RVV.