So yeah, it seems plausible P-224 and the B-* curves were generated at a later date.
2,790 karma · joined November 14, 2011
So yeah, it seems plausible P-224 and the B-* curves were generated at a later date.
How to encode VEX.vvvv:
0: VEX.vvvv must be 1111b.
1: VEX.NDS. Register-only source is encoded in VEX.vvvv where
the content of source registers will be preserved.
VEX.DDS. The second register operand is encoded in VEX.vvvv
where the content of first source register will be overwritten
by the result.
VEX.NDD2. The second destination register operand is encoded in
VEX.vvvv for instructions with 2 destination register operands.
For assembler, there are no difference between VEX.NDS, VEX.DDS
and VEX.NDD2.
2. VEX.NDD. Register destination is encoded in VEX.vvvv for
instructions with 1 destination register operand.
3. VEX.LWP. Register destination is encoded in VEX.vvvv and one
of the operands can access a memory location.
[1] https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;... return n ? (1u + popcount(n & n - 1u)) : 0u;
which both Clang and GCC promptly optimize to a single popcnt. f(n) = 5226577487551039623 + 1537228672809129301*(1669582390241348315^n + 636260618972345635^n) + 3689348814741910322*(725554454131936870^n + 194643636704778390^n + 1781303817082419751^n + 1910184110508252890^n) mod (2^61-1).
This involves 6 exponentiations by n with constant bases. Because in fizzbuzz the inputs are sequential, one can further precompute c^(2^i) and c^(-2^i) and, having c^n, one can go to c^(n+1) in average 2 modular multiplications by multiplying the appropriate powers c^(+-2^i) corresponding to the flipped bits.The main difference there seems to be that GCC treats the if() as unlikely to be taken while the for() as likely.
Currently the best attacks on NTRU, Kyber, etc, are essentially the same generic attacks that work for something like Frodo, which works on unstructured lattices. And while the resistance of unstructured attacks is pretty well studied at this point, it is not unreasonable to suspect that the algebraic structure in the more efficient lattice schemes can lead to more efficient attacks. How efficient? Who knows.
The floating point schedulers have a slow region, in the oldest entries of a scheduler and only when the scheduler is full. If an operation is in the slow region and it is dependent on a 1-cycle latency operation, it will see a 1 cycle latency penalty.
There is no penalty for operations in the slow region that depend on longer latency operations or loads.
There is no penalty for any operations in the fast region.
To write a latency test that does not see this penalty, the test needs to keep the FP schedulers from filling up.
The latency test could interleave NOPs to prevent the scheduler from filling up.
Basically, short vector code sequences that don't fill up the scheduler will have better latency.[1] https://www.amd.com/content/dam/amd/en/documents/processor-t...
No. The lock calls are only done during initialization, in case two threads run the initialization concurrently while the guard variable is 0. Once the variable is initialized, this will always be skipped by "je .L3".
No anomaly:
2,190,954,207 cpu_core/cycles:u/ ( +- 0.14% )
4,412,790,656 cpu_core/uops_issued.any:u/ ( +- 0.11% )
39,386,389 cpu_core/exe_activity.1_ports_util:u/ ( +- 11.57% )
2,121,401,346 cpu_core/exe_activity.2_ports_util:u/ ( +- 0.11% )
6,015,432 cpu_core/exe_activity.exe_bound_0_ports:u/ ( +- 8.87% )
593,599,670 cpu_core/uops_retired.stalls:u/ ( +- 0.85% )
Anomaly: 4,357,567,336 cpu_core/cycles:u/ ( +- 0.15% )
4,448,899,140 cpu_core/uops_issued.any:u/ ( +- 0.26% )
2,107,051,688 cpu_core/exe_activity.1_ports_util:u/ ( +- 0.14% )
1,106,699,503 cpu_core/exe_activity.2_ports_util:u/ ( +- 0.13% )
1,129,497,409 cpu_core/exe_activity.exe_bound_0_ports:u/ ( +- 0.42% )
2,502,226,997 cpu_core/uops_retired.stalls:u/ ( +- 0.38% )
Noise from the surrounding code aside, we see the same number of uops issued. However in the anomaly case, ~1/4th of the cycles are spent with no uops being executed, 1/2 are spent with only 1 uop being executed, and around 1/4 of the cycles have 2 uops being executed. I expected 0 and 2 being 50/50, consistently with there being one cycle stall, but if the uops are desynched and issued one cycle apart it would also explain the 1 being so prominent.To confirm this I add an LFENCE at the start of each loop iteration to serialize the pipeline and try to ensure that each SHLX pair is issued in the same cycle. And it works:
4,581,269,346 cpu_core/cycles:u/ ( +- 0.10% )
4,556,347,404 cpu_core/uops_issued.any:u/ ( +- 0.12% )
133,363,872 cpu_core/exe_activity.1_ports_util:u/ ( +- 7.73% )
2,082,838,530 cpu_core/exe_activity.2_ports_util:u/ ( +- 0.24% )
2,165,817,614 cpu_core/exe_activity.exe_bound_0_ports:u/ ( +- 0.06% )
3,090,362,239 cpu_core/uops_retired.stalls:u/ ( +- 0.16% )
Now the uops are split between 0 and 2 executed per cycle, as theorized.This may or may not be consistent with one CQO uop being executed once a cycle as expected, and one SHLX uop taking a a spot (stalling for one cycle?) for 2 cycles, resulting in a runtime of (x/2 * 1 + x/2 * 2)/2 ~ x/1.33 cycles.
Besides the shifts and rotations BZHI and BEXTR are also affected. While they are not a shift per se, it makes sense that it would be implemented with the same circuitry (e.g., BZHI is dst = arg1 & ~(-1 << arg2)). BEXTR in particular goes from 2 to 6 cycle latency!
Another thing I'm noticing is that the affected instructions are all p06 shift ops. You can alternatively implement rotation using SHLD r,r,c but this is a p1 operation and I have not seen any slowdown from this issue.
for(auto&& e : range) {
std::print("{}" e);
join {
std::print(", ");
}
}
where join {} is effectively syntax sugar for if(std::next(__first) != __last) {}.The fmt library makes this sort of task easier by providing the join adaptor; this example would become
fmt::print("{}", fmt::join(range, ", "));This would render the shuffles unusable, because you'd unpredictably have them costing either 1 uop or cycle to taking 10-30 uops/cycles depending on which core you are on at the moment. A situation similar to PEXT/PDEP, which cost almost nothing on Intel and dozens of cycles on AMD until a couple generations ago.
Why does Zen 4 not have this problem? First, they're only double-pumping instead of quad-pumping. Secondly, while most of their AVX-512 implementation is double-pumped, there seems to be a full-length shuffle unit in there.