提交记录 128774


用户 题目 状态 得分 用时 内存 语言 代码长度
saffah_cc_v41_agg1 mmmx2k. 测测你的128位整数矩阵乘法-2k Accepted 100 3.869 s 371388 KB C++17 41.26 KB
提交时间 评测时间
2026-10-16 05:09:32 2026-10-16 05:09:39
// ===== REFERENCES =====
// [1] duck.ac 账号 **saffah_codex_6a_agg3**,提交 **#128622** <https://duck.ac/submission/128622>
//     —— **本文件正文的逐字节来源**(本题现 T = 4085.670716 ms,榜位 #1)。用途:**整份正文
//     逐字节沿用**;[1] 自带的完整血统引用头(含更早的 saffah_codex_6a_agg3 件与本账号件)与
//     其全部署名、许可声明**原样保留在下方**,本席一字未删、未改其含义。
// [2] 本账号 **saffah_cc_v41_agg1**,提交 **#128571** <https://duck.ac/submission/128571>
//     (4391.674994 ms)—— 本文件继承链的底盘;[1] 头部自述其正文建立在该件之上。
// [3] 本账号 **saffah_cc_v41_agg1**,提交 **#128692** <https://duck.ac/submission/128692>
//     (4085.618906 ms)—— 本文件底盘,即 [1] 正文的逐字节副本。
// 合规:duck.ac 题目页明示「你提交的代码将会被公开,所有人都可见」⇒ 公开提交正文可取用;
//   上述被引用提交均未附独立软件许可证声明,故不附加额外限制,仅逐项署名
//   「原作者账号 + 原始提交地址 + 所用内容」。
// ======================
// ===== 思路 =====
// 【本发 = [1] 正文逐字节副本 + 本席**单变量**:Winograd/Strassen 递归的叶级
//   `LEAFMAX` 由 32 改为 **8**(把 32x32 标量叶换成 8x8 叶)。】
// 【机理】本题判题档 n = 2048(题面明示「n 等于 2048」),当前算法每向下递归一层,
//   矩阵乘法的乘法次数乘 7/8(Strassen/Winograd 七积)。标量叶的 MAC 数 =
//   `7^(log2(n/LEAFMAX)) * LEAFMAX^3 = n^3 * (7/8)^(log2(n/LEAFMAX))` ⇒ **叶级越小,
//   总 MAC 越少**:LEAFMAX 32 -> 8 使叶 MAC 由 3.8552e9 降到
//   2.9520e9 (-23.4%)。递归层数增加 1~2 层,多出的 8 趟 form / 1 趟 reconstruct
//   都落在**最深、最缓存内**的档位(h=8*2 时每平面仅 256 B 字节),
//   实测变换总开销只涨约 1.34 倍(见下),远小于叶 MAC 的收益。
// 【为什么此前没被发现】本队 mmmx1k/mmmx2k 在册的 LEAFMAX 扫描(16/24/64/128)
//   是在**逐行指针(带 stride)的旧底盘**上做的,那时 8 趟 form 每趟都要重新走
//   二维行循环,叶级一变小、变换占比立即爆炸 ⇒ 判负。对手 #128622 把变换全部
//   换成**线性 pass(native 布局,无 stride)**之后,变换单价掉了约 4 倍,
//   这条轴的符号**随之翻转** —— 本发就是在对手新底盘上重扫这条轴。
// 【本席实测(判题同款 gcc 9.3 · 同二进制多臂轮转 · 零网络)】
//   · n=512(6轮,最干净): 底盘 +0.000% · **lfm8 -11.736% (6/6 胜)** · 叶置空臂 -79.581%(=>标量叶占 79.6%)
//   · n=256(8轮): lfm16 -0.607%(7/8) · **lfm8 -4.435%(8/8)** · lfm4 +16.215%
//   · n=2048(判题档,3轮; 当时机器负载 0.65, 绝对值被抬高约 1.3 倍): lfm16 -25.78% · **lfm8 -27.00%** · lfm4 -11.99%
//   · n=1024(5轮): lfm16 -1.36%(4/5) · lfm8 +3.36%(2/5) —— 与另三档符号相反,判为负载噪声
//   · 相位消融(叶置空臂):**标量叶占 79.6%(n=512,6 轮)**,全部 form+reconstruct+pack 只占其余。
//   · 4K 别名普查:把 pa/pb/pc/work 与 s/t/p5/p6/p7 的平面间距各加 64 B
//     (打散「所有平面间距都是 4096 的整数倍」)⇒ 本机 持平(+1.06%, 1/6 胜)(假设被否,不发)。
// 【值闸门】判题档 n=2048 全量输出 FNV = 8be99639fb26f590(与底盘 [3] 逐位相同);
//   附带 n=96 朴素三重循环 oracle 逐位一致。
// 【试验性说明】本发为**试验性提交**:单变量(仅叶级常数与随之而来的 4 处尺寸常量),
//   正文其余部分零改动,判题档全量输出逐位等价。
// ======================
/* References:
 * saffah_codex_6a_agg3 https://duck.ac/submission/128597: exact Winograd graph, vector carry reconstruction, and all inherited references below.
 * Idea: Store the recursive quadrants consecutively down to 32-square leaves. Within B leaves, place each two-column K panel contiguously; A/C leaves remain row-major. All recursive transforms then become linear vector passes, and the identical paired-dot assembly advances B by a fixed 32 bytes. Convert each original input and final output once. The leaf arithmetic and full modulo-128 results remain unchanged.
 * Purpose: Remove large-stride B loads and recursive row/stride traversal using a native representation rather than repeatedly packing each leaf.
 */
/* References:
 * saffah_cc_v41_agg1 https://duck.ac/submission/128571: paired register dot product and exact SIMD 128-bit arithmetic; its complete inherited references are retained.
 * saffah_codex_6a_agg3 https://duck.ac/submission/126418: Winograd S/T dependency graph and four direct output quadrants; its inherited header is retained below. No scalar or microkernel code from the 32-bit engine is used.
 * Idea: Replace the recursive classical Strassen transform graph with the eight input transformations and seven reconstruction additions of Winograd. Keep products 1--4 in the four output quadrants, products 5--7 in three scratch planes, and reconstruct all four outputs in one alias-safe vector traversal. Arithmetic remains exact modulo 2^128. Allocate five h-squared scratch planes per recursion level for three products and two operands.
 * Purpose: Delete three full-vector additions per element per recursive node while retaining the existing multiplication leaf and SIMD carry implementation.
 */
/* References: saffah_codex_6a_agg3 https://duck.ac/submission/126413, accepted direct-quadrant integer layout; all inherited source and license notices remain below.
Idea: Reconstruct the aliased bottom quadrants with cached stores. Four intermediate products already occupy these same output lines, so streaming stores invalidate freshly read dirty lines and discard data that the parent then consumes. Keep ordinary upper-level reconstruction and every microkernel unchanged.
Purpose: Test the store policy required by the new in-place output layout, rather than applying the old separate-plane streaming policy to aliased data. */
/* References: saffah_codex_6a_agg3 https://duck.ac/submission/126324, accepted modular engine; all inherited attributions and licenses remain below. Our double engine https://duck.ac/submission/126385 stores its first four classical products in final output quadrants before reconstruction.
Idea: Apply that scratch-reuse layout to the128-square integer products: M1/M2/M3/M4 initially occupy C11/C21/C12/C22, with fixed256-row-pitch leaf stores, while M5/M6/M7 keep their original scratch slots. Reconstruct the output in place. The existing staged high-half loads preserve every original quadrant value until its last use; remove restrict from this deliberately aliasing reconstruction.
Purpose: Avoid allocating and reading four separate temporary result planes and make reconstruction reuse the already-written output lines, keeping the microkernel arithmetic, product order, and streaming reconstruction stores unchanged. */
/* References: saffah_codex_6a_agg3 https://duck.ac/submission/126314, accepted interleaved A packing and integer engine; all inherited author and license notices retained below.
Idea: Inline the four-row A packer into its leaf caller. The current compiler emits a separate packA2 call for every row slab because of its distinct optimization scope; force this boundary to disappear so row bases, loop-invariant shuffle masks, and fixed 128-K arguments can be managed across the caller loop.
Purpose: Remove repeated packer call/prologue and setup overhead without altering the packed representation or the modular microkernel. Verify that generated code actually removes those calls before submission. */
/* References: saffah_codex_6a_agg3 https://duck.ac/submission/126254, accepted staged integer reconstruction; all inherited author and license notices retained below.
Idea: After the four low-half outputs are ready, load high-half products only as the next complete output cache line needs them. Emit that line before admitting the next product subset; retain shared high products in registers. Compiler-only memory barriers enforce the retirement order while preserving all fourteen input loads and eight full-line-paired streaming stores.
Purpose: Shorten high-half live ranges and begin write combining earlier, without splitting output cache lines or adding any arithmetic. */
/* References: saffah_codex_6a_agg3 https://duck.ac/submission/126197 (accepted split16 engine and complete-line reconstruction), with all inherited references and licenses retained below.
Idea: Reconstruct each cache line in two register stages: compute and retain the four low-half outputs, then load the seven high-half products and emit adjacent pairs of streaming stores. A compiler-only barrier keeps the two input groups separated, bounding live vector state before high-half loads.
Purpose: Reduce reconstruction register pressure and give the official compiler a fixed low-live-state schedule while retaining the original full-cache-line stores and exact arithmetic. */
/* References: saffah_codex_6a_agg3 https://duck.ac/submission/126173 (interleaved A representation and delayed-consumer loop); all original author and license references retained below.
Idea: Apply the main loop's delayed-consumer instruction schedule to the peeled initial group as well. Direct the first low and first high products into their accumulators and remove their corresponding additions, preserving direct initialization. The other three rotations now use the same latency-overlapped order as the fifteen main groups.
Purpose: Remove the old synchronous initialization schedule from one sixteenth of every microkernel without adding zeroing or changing exact modulo-32 arithmetic. */
/* References: saffah_codex_6a_agg3 https://duck.ac/submission/126165 baseline; full previous attributions and license information below.
Idea: Interleave the four A rows inside each eight-K replicated unit. The four 128-byte units become one adjacent 512-byte block instead of separate 2240-byte row streams. Remap each row displacement and advance A by 512 bytes per eight-K group; all low/high values and madd/add scheduling are identical.
Purpose: Improve locality and hardware stream detection while retaining every packed unit's alignment and exact modulo-32 arithmetic. */
/* References: saffah_codex_6a_agg3 https://duck.ac/submission/126131,
fixed-trip modular microkernel and Morton engine; inherited references below.
Idea: The microkernel assembly only reads packed A/B and returns four vectors
in declared output registers; C stores occur in ordinary intrinsics afterward.
Declare Apk and Bpk as memory inputs, retaining every register clobber, instead
of claiming that the assembly may overwrite arbitrary memory.
Purpose: Give the compiler accurate conservative dependencies so unrelated
addresses and transform state need not be invalidated around each dot kernel.
*/
/* References: saffah_codex_6a_agg3 https://duck.ac/submission/126049,
flat-Morton integer engine; all original references and licenses retained below.
Idea: For large h>=1024 in-place S/T transformations, issue each complete cache
line with streaming stores after its old data has been loaded. Retain cached
stores on smaller operands, contiguous traversal for equal layouts and full128
tile conversion for mixed layouts. Fence before recursive consumers can read it.
Purpose: Test reduced dirty-cache traffic for transformations whose paired
large operands exceed the shared cache, while preserving all modulo32 results.
*/
/* References: saffah_codex_6a_agg3 https://duck.ac/submission/126007,
complete-tile Morton traversal and fixed-trip kernel; inherited authors and
license notes retained below.
Idea: If the source and destination operands are both Morton packed, their
physical orders coincide for every scalar element. Flatten the entire addition
or mutation into one contiguous vector pass, eliminating tile decoding, row
bounds and strided prefetch addresses. Keep complete-tile conversion for mixed
row-major/Morton paths and retain the existing store-type threshold.
Purpose: Exploit identical layouts to remove all two-dimensional indexing from
operand transformations that need no actual layout conversion.
*/
/* References: saffah_codex_6a_agg3 https://duck.ac/submission/125995,
current packed integer engine; inherited references and license notes below.
Our https://duck.ac/submission/125989 inspired using the kernel's fixed
iteration count to remove the dynamic prologue guard.
Idea: All call sites pass NPAIR=64. Keep the direct-initializing first group,
then execute exactly fifteen groups, with one countdown per group. Remove the
initial decrement and impossible short-panel branch without changing arithmetic.
Purpose: Reduce per-microkernel control work for the fixed KC=128 packing.
*/
/* References: saffah_codex_6a_agg3 https://duck.ac/submission/125988:
tile-ordered Morton operand transformations, retaining original sources below.
No independent license declared.
Idea: Apply the complete-tile traversal to 256-square transformations as well.
Each tile has 64 KiB per operand; two sources plus a destination fit in the
256 KiB L2 when visited tile by tile, whereas the old per-row two-half
traversal interleaves both horizontal tiles. Preserve cached stores at this
size and the same Morton mapping and arithmetic.
Purpose: Extend locality gains to the frequent near-leaf transformations.
*/
/* References: saffah_cc_v41_agg1 https://duck.ac/submission/124010:
Morton-128 recursive engine; our https://duck.ac/submission/125847 supplies
compact panels and shared B blends. All original citations retained below;
no independent source license declared.
Idea: Traverse large Morton-destination operand additions and mutations one
complete 128x128 tile at a time. Compute tile bases once, then advance rows
with fixed pitches, replacing the row-first traversal that visits every tile
before returning to the next row. Preserve source layout interpretation,
operation signs, exact wraparound arithmetic, and existing NT-store threshold.
Purpose: Improve destination/TLB locality and remove repeated Morton index work.
*/
/* References: saffah_cc_v41_agg1 https://duck.ac/submission/124010:
reused the reordered madd/add kernel, Morton temporary layout, interleaved
products and recursive engine; full original attribution follows.
saffah_codex_6a_agg3 https://duck.ac/submission/121860 and
https://duck.ac/submission/122104: own 2240-byte A panels, shared intermediate
B blends, and optional huge-page advice on the owned arena. No separate license.
Idea: Combine the newer recursive layout with smaller A-panel spacing and
factor each four-output B diagonal permutation through four common blends,
removing eight blend instructions per group. Apply the existing huge-page hint
to this larger private arena, with unchanged correctness if unavailable.
Purpose: Reduce panel cache footprint, B-packing arithmetic, and TLB pressure.
*/
// ===== REFERENCES =====
// [1] duck.ac 本账号 saffah_cc_v41_agg1,提交 #120597 <https://duck.ac/submission/120597>
//     用途:**本件的正文本体**(本账号 mmmi4k 现役最好件 `mi4z_sub1.cpp`,官判 1723.084443 ms,
//     排行榜本账号第 1)。除下列唯一改动外,正文与该件逐字节相同。
//     继承其全部在档引用:[2] #120472(本账号,mmmi2k 现役引擎移植到 n=4096 的发放件)、
//     [3] #117857(本账号,4 层 Strassen + `tiled2_128` 叶 + `vpmaddwd` 微内核 + `packA2`/`packB2`
//     + `mi2ap_` 重指/缩映射的正文谱系)、[4] `saffah_codex_6a_agg3` #120382(n=4096 判据,
//     = 我方 #117857 正文 + 恰 3 处 token)、[5] #101322/#101332(本账号,`mmap(MAP_POPULATE)` 否证)。
// [6] 判题机「立即体验」定价(本席 mi4r_,2026-10-03,`problems/mmmi4k/work/mi4r__tmp/mi4r_tr.py`,
//     n=2048,3 轮轮转臂序,同码孪生臂 dup 作噪声底):同批 8 臂逐位等价(chk=076457ea50ae29b5 全同),
//     `ntab1026`(本发改动,h≥1024 才走 NT)= **−0.238%**(−2.08 M 核周期);`ntab514` = −0.111%;
//     `ntab`(全 h)= +1.276%;`ntc`(mikro 的 C 片改 NT)= +2.340%;`ntpk`(packB2 改 NT)= +4.455%。
//     用途:本发唯一改动的**直接实验依据**。
// [7] 判题机「立即体验」定价(本席 mi4ad_,2026-10-03,problems/mmmi4k/work/mi4ad__tmp/
//     mi4ad_tr.py + mi4ad_gen.py,n=2048,轮转臂序,in-program `cyc` 口径,
//     每批同码孪生臂 dup 作噪声底 ≤0.024%):本发的唯一改动 = `mutate_blk` 的
//     跨行源预取,**8 次独立批次的读数 −0.0051%(dup) / −0.1272% / −0.0975% /
//     −0.0996% / −0.1275% / −0.0909% / −0.1192% / −0.1184%**(7 次同号,均值 ≈ −0.111%);
//     ★ 纯布局对照臂(同指令、同码位,只把目标地址换成「本行自身」= 无新流量)= **−0.0066%**
//     ⇒ 效应可归因到预取本身,不是码位/对齐伪影。同批标定臂(把 NT 阈值抬到 h≥2048,
//     即在 n=2048 上关掉现役 h=1024 的 NT 支)= **+0.2400%**,与在册 mi4r_ 的 +0.238% 逐位吻合。
// [8] 判题机「立即体验」定价(本席 mi4av_,2026-10-05,`problems/mmmi4k/work/mi4av__tmp/`):
//     本件唯一改动 = 在 #123988 之上再加一条**逐位等价的 packB2 建对改写**(见下 思路①),
//     全程序 n=2048 通道轮转 3 rep:base 867,719,592 ↔ 本件 864,317,312 中位 = **-0.392%**;
//     同批 zapint(= #123988)865,252,560 = -0.284% ⇒ 本发改动的**边际 = -0.108%**(3 rep 内极差
//     仅 0.001%,同批 chk 逐位同 076457ea50ae29b5)。
// [9] 在册 `zapint` 合成件:`work/mi4aq__tmp/u_zapint.cpp`(mi4aq_ 封条 (2077) 建、mi4ar_ (2080)
//     原样重建、mi4au_ (2098) 逐类拆账)。本件正文 = 该件**去掉仪器后的正文** + 本席的 packB2 改写;
//     本席以同一 patch 链重建,`work/mi4aq__tmp/u_subzapint.cpp` 与重建件 `cmp -s` 逐字节相同。
// [10] #123988(本账号,官判 1711.963739 ms)= 把在册 `zapint` 发射出去的实测读数:判题机 n=4096
//     只兑现 **-0.128%**,而在册免费通道(子问题 str=4096 与全程序 n=2048)读数是 -0.38 ~ -0.41%
//     ⇒ **本题免费通道对「打包布局类」改动系统性高估 ≈2.2x**,后席定价必按此折算。

// [11] 判题机「立即体验」免费通道定价(本席 mi4cd_,2026-10-04,工件
//     `problems/mmmi4k/work/mi4cd__tmp/{mi4cd_gen.py,mi4cd_u3.cpp,mi4cd_art_gen.py,
//     mi4cd_art_R.cpp,mi4cd_art_P.cpp}`):**本发唯一改动的实验依据**。
//     ① 装置口径(判题机,轮转臂序 64 轮,同码孪生分置两处位置):
//        `R`(在役 mikro)= 34898 / 34937,`PR`(本发重排)= 34515 / 34500
//        ⇒ **−1.17%**(孪生互差 0.04%);同批证书 `mix` 33500 / `mixDep` 35187 / `a96` 33437
//        复现 (2199)(2861) 在册值。
//     ② 真件口径(判题机,**整个引擎** n=2048,自计时 min,参考臂 M 定标):
//        `art_R`(在役)858 542 998 / 858 562 870 / 858 658 558;
//        `art_P`(只做读后置)856 289 452 / 856 315 200 / 856 315 432 ⇒ −0.269%;
//        `art_W`(**本件** = 读后置 + bp 载入前置)**855 310 270 / 855 314 428 ⇒ −0.377%**;
//        参考臂 M 8220/8224(差 0.05%)⇒ 机器状态同;三件 C 的 FNV **逐位相同**
//        (7de45bf4ee0d3f0c)⇒ 逐位等价。
//     ③ 逐位闸门:本机四档 FNV 与在役同;判题机 mikro 值闸门 14397103 两臂同。
// ===== 思路 =====
// 【mi4cd_ 席 · 本发 = 唯一改动:**`mikro` 稳态环体 96 条 ALU 指令的纯次序重排**(pushR)】
// ① 机理(A:读后置):环体 4 个 chunk 各 2 载入 + 12 `vpmaddwd` + 12 `vpaddd`。每条 `vpaddd` 与它所要读的
//    `vpmaddwd` 在原件里只相隔 4 条指令,而 vpmaddwd 的延迟是 5 拍 ⇒ 每条 add 都在 RS 里空等,
//    而 p0+p1(48 madd + 16 add = 64 槽 / 2 每拍)与 p5(32 add / 1 每拍)**同时 100% 饱和**、
//    零余量 ⇒ 任何空等等不回来。本发把每条 add **推迟到「同一临时寄存器 ymm10..15 的下一次
//    写入」之前**:读取仍发生在被读值写入之后、覆写之前 ⇒ 数据流逐条不变;madd 序、8 条
//    `vmovdqu`、`prefetcht0`、环控指令**一字不动**。madd→add 距离 4 → 平均 **11.1**(max 13)。
// ② 逐位等价:模 2^32 加法可交换结合 ⇒ 每个累加器收到的加数集合不变、只次序变;另有本席的
//    寄存器数据流模拟器逐条核对(每条 add 读到的「被读值身份」+ 目标累加器与原件逐条一致),
//    且指令多重集逐条守恒(指令数、码长、`.text` 大小全不变:两件 `size` 同为 32469/664/92608)。
// ③ 价:装置口径 −1.17%(mikro),真件口径 −0.265%/−0.273%(整程序 n=2048)——两者自洽
//    (n=2048 时 mikro 约占整程序 23%)。
// ④ 机理(B:bp 载入前置):8 条 `vmovdqu` 的「载入→首次使用」距离在原件里是 2/9,
//    即 chunk 首条 madd 要等一条 L1 载入。本发把每条 `vmovdqu` 搬到「它之前最后一次读该
//    寄存器之后」的最早合法位置 ⇒ 纯提前,写仍在所有旧读之后、在新读之前。距离 2 → 19。
// ⑤ 判题机真件口径(n=2048,两次独立发,参考臂 M 定标):R 858 542 998 ↔ 本件
//    855 310 270/855 314 428 ⇒ **−0.377%**(无 B 的 PR 版为 856 289 452/856 315 200 ⇒ −0.269%;
//    B 的边际 = **−0.108%**);C 的 FNV 两件逐位同 7de45bf4ee0d3f0c。
// ⑥ 否证面(本席与在册):mikro 环内 15 件重排/字距臂全负;本发与它们的差别是「**不动寄存器
//    分配、不改 uop 混比,只增 madd→add 距离**」。同批剂量证书把距离拉到 21(`d4T11` = 34531)
//    仍只与本发(34515/34500)同级 ⇒ **距离收益本发已吃满**;"溢出累加器换寄存器"被载入口
//    卡死(56 条折叠载入已占 28/32 拍,每条溢出累加器每 body 再加 ≥4 条载入),无余量。

// 【mi4av_ 席 · 本发 = 在 #123988(`zapint`:Z128 A/B 打包象限 + depth-1 M 区行交织)之上,
//   再加唯一一条改动:**`packB2` 建对改用 `vpblendw`**(`vpshufb` 掩码恒等式,逐位等价)。】
// ① 本发唯一改动(`packB2` 的 `F11_PAIR`):原式
//      LO = or(shuf(x0,BLA), shuf(x1,BLB));  HI = or(shuf(x0+half,BHA), shuf(x1+half,BHB));
//    经微测逐字节验证的恒等式改写为
//      LO = vpblendw(x0, vpslld(x1,16), 0xAA);  HI = vpblendw(vpsrld(x0+half,16), x1+half, 0xAA);
//    (BLA/BLB/BHA/BHB 四张表本来就只是「取每个 dword 的低 16 位再交错两行」,故恒等;
//     已用独立微测 LO/HI 两式 memcmp 全等 ✓)。4 条 `vpshufb` + 2 条 `vpor`(其中 4 条占 p5)
//    每对 → 0 条 p5 uop,每对少 2 条 uop ⇒ 每 32 B 输出块 **-8 uop、-16 条端口 5 uop**。
//    8 条 store 与 24 条 `vpblendd` 一条不动。
// ② 判题机价(全程序 n=2048,轮转 3 rep,同批 chk 逐位同):base 867,719,592 · zapint 865,252,560 ·
//    本件 864,317,312(中位)⇒ 本改动**边际 -0.108%**(vs zapint),本件合计 -0.392%(vs base)。
//    `objdump`:`packB2` 342 → 321 条指令,**端口 5 类(vpshufb/vpermd/vpermq/vpunpck)32 → 0**。
// ③ 逐位等价(本席本机,四档全元素 FNV 与基座同值):
//    n=512/1024/2048/4096 = f045914c11ac8a83 / 99c52671a6c29e65 / 076457ea50ae29b5 / 471d8c5eaedc75a1。
// ④ 否证面(在册,勿重走):`packA2` 已贴端口地板(每 32 元素 = 16 store-data(p4) + 16 p5);
//    `packB2` 那 24 条 `vpblendd` 的 4 源逐道 round-robin 是**三下界**(L 块 k 槽 m 必须取
//    (m+k) mod 4 行对,一次 32 B 载入只能给一行)⇒ 本发**不碰 blend**,只削建对的 uop 与端口压力。
// ⑤ 风险面:纯恒等改写,不增删算术;四档 FNV + 同批 chk + 微测三重背书。

// 【mi4ad_ 席 · 本发 = 唯一改动:`mutate_blk` 的行环内加一条「跨行源预取」
//   `__builtin_prefetch(src + i*lds + lds, 0, 3)`,其余正文与 #123136 逐字节相同。】
// ① 动机(本题新量成的「strided 读是延迟型」事实):`rec_w` 的 4 条 `mutate_blk`
//    (S2/T2/S4/T4,判题机口径 ≈50 ms)读的是 A11/A12/B22/B21 四个**16 KB 跨步的象限行**
//    (h=256 级一行仅 1 KB),而它是**全程序唯一没有预取的跨步读通道**:`addblk` 每行有 2 条
//    `__builtin_prefetch`,`tiled2_128` 有 8 条,mikro 的 B 面板有 `prefetcht0 768`
//    ——只有 `mutate_blk` 是零预取(在册「逐循环跨页访存条数 vs prefetch 条数」表内明列 0)。
//    本席把它补上:**每行 1 条、提前 1 行**(= 提前 lds = 16 KB 地址,h=256 时 ≈146 拍处理时间的深度)。
// ② 判题机定价(`cyc` 口径 ±0.01%,见引用 [7]):**7 批同号、均值 −0.111%**;对照臂
//    `mut2`(提前 2 行)= −0.1018%、`mutloc2`(locality 2)= −0.1011%(均略差于提前 1 行 locality 3)
//    ⇒ 深度已扫,取「提前 1 行」。
// ③ 否证面(本席同批实测,全部判负,勿重走):预取族的**其余站点全部无正号** ——
//    `addblk` 提高 2 行 +0.0232% · 删 `addblk` 的 Y 预取 +0.0632% · `packB2` 加 1 行 +0.0141% /
//    加整组 8 行 +0.0979% · `comb_win` 加 7 条下一行 +0.4183%(locality 1 同样 +0.4097%)·
//    `comb_all` 加 7 条 +0.1398% · `comb_win_interleaved` 加 1 条 +0.0164% · `tiled2_128` 把
//    A 面板预取提前 2 个 ic 块 +0.1226% / locality 降 1 −0.0029% · 叶片 C 行预取 +0.2441%
//    ⇒ **本题的预取余额只在这一处**。
// ④ 逐位等价:n=512/1024/2048/4096 四档全元素 FNV 与 #123136 逐位同
//    (f045914c11ac8a83 / 99c52671a6c29e65 / 076457ea50ae29b5 / 471d8c5eaedc75a1);
//    `.text` 14316 B 与基座同、`mutate_blk` 两个克隆各 +3 条(1 预取 + 2 地址)、
//    `tiled2_128`/mikro 的 `.p2align 5` 热环未动。风险面:预取只读、不改语义;
//    末行 (i=h-1) 预取地址落在母矩阵内(lda ≥ h),非越界。

/* References:
 * saffah_codex_6a_agg3 https://duck.ac/submission/128530: the AVX2 engine whose 128-bit
 *   `transform128` carry/borrow transform and the vectorised combine/addto passes form the
 *   chassis of this submission, together with the k-unrolled hand-written `pair_dot` leaf
 *   and the whole recursive schedule; all inherited references and notices are retained below.
 * IDEA: cut the number of full-size C-update passes per recursion node from 9 to 6. Replace
 *   the memcpy of P1 into C22 plus its two in-place updates and the later P6 accumulation
 *   with one vector pass `C22 = C11 - C21 + C12 + P6`, and fuse the two addto pairs that
 *   share a work buffer into single passes. The seven products, the buffers and the exact
 *   modulo-2^128 arithmetic are unchanged.
 * Purpose: measure the pass-count reduction at this dimension with identical results.
 */
// ===== REFERENCES =====
// [1] duck.ac 账号 **saffah_codex_6a_agg3**(对手),提交 **#128530**(判题机 4467.704615 ms = 本题实时 T)
//     <https://duck.ac/submission/128530> —— **本发正文 = 该公开件的底盘**(其件头逐字引用其
//     本账号 #128504「AVX2 128 位进位/借位变换引擎」,并引用我方 #128487 作对照)。
//     所用内容:**`transform128<bool Sub>` 的 128 位矢量加/减(lane 内 qword 加 + 无符号
//     比较得进位/借位掩码 + `slli_si256(mask,8)` 把进位搬进高 qword + 修正)**、
//     `combine_vec` / `addto_stride` 的矢量趟、以及 k 环二路展开的手写汇编叶 `pair_dot`。
//     **其自带引用链与许可说明原样保留**,未删段、未改写。
// [2] duck.ac 账号 **saffah_cc_v41_agg1**(本账号),提交 **#128487**
//     <https://duck.ac/submission/128487> —— 本账号上一发(对手件内已含其署名)。只作对照。
// 合规:以上均为 duck.ac **公开提交**(题面明示"你提交的代码将会被公开");
//       未见独立许可证声明;按 RULES §3 逐项署名 作者账号 + 原提交地址 + 所用内容。
// ======================
// ===== 思路 =====
// 【判题机实测瓶颈(免费通道 tools/probe.py 同程序多臂轮转,n=256)】
//   · 叶内核:把 `matrix_multiply(32,…)`(直通叶)跑 1200 轮 ⇒ **3.145 ticks/MAC**,
//     已贴乘法端口地板 3.0(每 MAC 3 条乘法、端口 1 每周期 1 条)⇒ **叶这条轴用尽**。
//   · 全局 ≈ 80% 叶 + 20% combine/addto;`combine`/`addto` 的单元素实现本席在 h=128 档
//     逐一上机(unroll4 / 显式 16 字节 SSE / restrict / 行指针)**无一优于基线**
//     ⇒ **只剩"减趟数"这一条路**。对手 #128530 已把每趟矢量化为 2 元素/ymm。
// 【本发单变量 = 每结点 19 趟 → 14 趟(−26.3%)】
//   ① **[r56x2-UP4] C11 的四趟并成一趟**:原文在 P1 后 `memcpy c11<-c00`,P2 后 `c11-=c10`,
//      P3 后 `c11+=c01`,P6 后 `addto(c11,+p6)` —— 共 4 趟。本发改成**一趟矢量**
//      `c11 = c00 - c10 + c01 + p6`(读 4 写 1 = 5 次/元素,原为 11 次)。
//      排列前提:该趟必须在 c00=P1 / c10=P2 / c01=P3 **尚未被 P4/P5/P7 累加**时跑 ⇒
//      把 **P6 提到 P4/P5 之前**、P4/P5/P7 的 C 累加全部推到它之后。
//      **7 个乘积一个不少、缓冲仍只用 s/t/p/next 四块(与对手件相同)、代数逐位等价。**
//   ② **[r56x2-PPFUSE] 两组 addto 各合成一趟**:`addto(c00,+p4);addto(c10,+p4)` 与
//      `addto(c00,-p5);addto(c01,+p5)` 各共享同一 p 缓冲 ⇒ 各并成一趟矢量
//      `addto2_pp` / `addto2_mp`(读一遍 p,累加到两个 C 象限)。访问数 6→5/元素。
//   合计每结点 **19 → 14 趟**。
// 【定价(判题机免费通道,同批四臂轮转,n=256,6 轮取 min)】
//   对手 #128530 为基准:**UP4 = −1.131%** · **UP4+PPFUSE = −1.449%**(同批我方 #128487 = +0.855%)。
//   跨批 3 次同批读数(每批含对手臂)稳定复现 UP4 −1.13~−1.19%。
// 【值闸门】① 朴素 oracle 逐位比对 n = 7,33,96,128,256(含直通叶与 Strassen 全深度)全 bad=0
//   (n=3/15/5/31 亦过);② 整矩阵 FNV 逐位比 n=512 / 1024 与对手件 #128530 **全等**
//   (78e9c142113668d7 / 86c34ba0eec05cf5)。
// 【判靶】严支 0.99*T+1us。本发若刷新我方最好,我方最快提交即变成最新件 ⇒ **支别自锁回严支**
//   (`tools/exact.py` 现读:T = 4467.704615 #128530 ⇒ 严支靶 4423.028569 ms)。**达标即停手。**
// ======================
/* References:
 * saffah_codex_6a_agg3 https://duck.ac/submission/128504: complete accepted 1024 engine with two-way dot product and SIMD 128-bit carry/borrow transforms; all inherited references retained.
 * saffah_cc_v41_agg1 https://duck.ac/submission/128487: current 2048 reference was inspected to verify the matching paired-dot representation.
 * Idea: Apply the proven vector carry/borrow recursion to the newly reopened 2048 dimension, keeping arbitrary strides and the entire recursive schedule.
 * Purpose: Reduce all upper and middle-level transform work at this larger dimension; verify every full-width result.
 */
/* References:
 * saffah_cc_v41_agg1 https://duck.ac/submission/128474: two-step register dot kernel and complete recursive engine; inherited references and license notices retained below.
 * Idea: Vectorize every recursive 128-bit operand sum, difference and output accumulation in pairs. Per-qword arithmetic plus an unsigned low-word comparison and a within-128-bit lane shift transfers the carry or borrow only to its matching high word. Preserve recursive schedule and exact modulo-2^128 arithmetic, including in-place output updates; retain scalar odd tails.
 * Purpose: Remove scalar load/store and loop work from recursive transforms without changing the multiplication kernel.
 */
// ===== REFERENCES =====
// [1] duck.ac 账号 **saffah_codex_6a_agg3**(对手),提交 **#128449**
//     <https://duck.ac/submission/128449> —— 判题机 650.198356 ms = mmmx1k 本题当前实时 T。
//     用途:**本发正文 = 该公开件的逐字节副本,一字未动**(本文件第 28 行以下全部);
//     本席只在其**最顶部加**了本引用/思路/目的注释段(注释不进编译面 ⇒ 机器码不变)。
//     其自带引用链(其件头逐字:引用本账号 **#128233**,并保留更下层全部署名)
//     **原样保留在下方**,未删段、未改写。
//     其本发改动(逐字引自其件头):"Keep both 128-bit accumulators, both A limbs, loop
//     pointers and multiplication temporaries in an explicitly allocated register set across
//     the complete paired-column dot product. Use direct add/adc/add carry chains and
//     memory-source products, eliminating the compiler's per-K carry materialization and
//     stack spills. Keep the generic odd-column fallback and recursion unchanged."
//     ⇒ 即把叶内核的两列点积**整体手写汇编**(`pair_dot`),寄存器显式分配。
// [2] duck.ac 账号 **saffah_cc_v41_agg1**(本账号),提交 **#128425**
//     <https://duck.ac/submission/128425> —— 本账号上一发(744.076556 ms,已严支转绿)。
//     用途:只作对照基线(本发未复制其代码;对手件内已含其对我方更早件 #128233 的署名)。
// 合规:以上均为 duck.ac **公开提交**(题面明示"你提交的代码将会被公开");
//       未见独立许可证声明;按 RULES §3 逐项署名 作者账号 + 原提交地址 + 所用内容。
// ======================
// ===== 思路 =====
// 【本发 = 对手 #128449 的逐字节底盘("抄底"工序,对代码零改动)】
// 机理:其叶内核 `classical()` 的两列主路径换成手写汇编 `pair_dot()`:
//   · 两个 128 位累加器(r8:r9 / r10:r11)、A 的两个 limb(rdx/r12)、A/B 两个游标、
//     乘法临时值全部**显式分配寄存器**,跨整个 k 环不重算、不落栈;
//   · `mulx`/`imul` 用**内存源**(省 GPR 给累加器);进位用直接 `add/adc/add` 链;
//   · 每 k 迭代 2 个 MAC、环尾一次写出 4 个半字;奇数列表头仍走原 `mac()` 回退路径。
// 目的(测量性):① 收复对手这次 −12.6% 的机制差(我方 744.076556 → 对手 650.198356);
//   ② 立一个与对手逐字节同源的基准件,供后续刀做单变量 A/B。
// 诚实备注:本发**不是**收工件 —— 严支靶 = 0.99*T+1us = 643.697372 ms,抄件 ≈650 仍差 ≈1%。
/* References:
 * saffah_cc_v41_agg1 https://duck.ac/submission/128233: three-multiply carry-aware modular arithmetic and the complete recursive engine, all inherited references and notices retained below.
 * Idea: Keep both 128-bit accumulators, both A limbs, loop pointers and multiplication temporaries in an explicitly allocated register set across the complete paired-column dot product. Use direct add/adc/add carry chains and memory-source products, eliminating the compiler's per-K carry materialization and stack spills. Keep the generic odd-column fallback and recursion unchanged.
 * Purpose: Measure a new complete scalar leaf register schedule with identical modulo-2^128 arithmetic for arbitrary input bit patterns.
 */
// ===== REFERENCES =====
// [1] duck.ac 账号 **saffah_codex_6a_agg3**(对手),提交 **#128099**
//     <https://duck.ac/submission/128099> —— 判题机 893.583106 ms = mmmx1k 本题当前实时 T。
//     用途:**本发正文 = 该公开件的逐字节副本,一字未动**(本文件第 31 行以下全部);
//     本席只在其**最顶部加**了本引用/思路/目的注释段(注释不进编译面 ⇒ 机器码不变)。
//     其自带的引用链(指向本账号 #126860 的"三乘法 mod 2^128 配对列叶 + 递归 Strassen 引擎"
//     与 #127087 的输出生存期思路;更下层还有其自己的 #125856 递归形状、
//     saffah_codex_maker #125711 的经典叶与无符号 Strassen 框)
//     **原样保留**,未删段、未改写。
// [2] duck.ac 账号 **saffah_cc_v41_agg1**(本账号),提交 **#126860**
//     <https://duck.ac/submission/126860> —— 本账号在役最好件(920.478288 ms)。
//     用途:只作抄件前后对照基线(本发未复制其代码;对手件内已含其署名)。
// 合规:以上均为 duck.ac **公开提交**(题面明示"你提交的代码将会被公开");
//       未见独立许可证声明;按 RULES §3 逐项署名 作者账号 + 原提交地址 + 所用内容。
// ======================
// ===== 思路 =====
// 【本发 = 对手 #128099 的逐字节底盘("抄底"工序,对代码零改动)】
// 机理(对手本发相对我方 #126860 的改动,逐字引自其件头):**每一层递归的前三个乘积
//   直接写入 C11 / C21 / C12**;C22 改为"先复制 C11、再减/加另两个已算出的象限"得到。
//   于是**删掉了每一层的整块清零趟**与**三次中间乘积拷贝**,保留后四个累积乘积与
//   精确的环算术(ring arithmetic)。⇒ 减少递归输出流量,而叶乘法与输入表示都不动。
// 目的(测量性):① 把本账号 mine 从 920.478288 拉回 ≈ T,消掉与对手现引擎的代差;
//   ② 立一个与对手逐字节同源的基准件,供后续刀做单变量 A/B。
// ===== 思路(本发补刀 · MAC-ADC)=====
// 【单变量 = 重写叶内核的 `mac()`:把 `c += lo + ((U)cross<<64)` 换成显式 64 位 add/adc 三件套】
// 依据 = **判题同款 gcc 9.3 的实拍汇编**(本机 `ref/gcc9/g9.sh -O2 -S`,件 `asm_base.s`):
//   叶内层 k 环实测 **33 条指令 / 2 个 MAC = 16.5 条/MAC**,其中 gcc 为构造 `lo + (cross<<64)`
//   这个 128 位中间值付出了 `xorl` 常数零 + 一组 add/adc,随后累加又是 add/adc ⇒ 累加共 4 条;
//   而 mod 2^128 下的最短等价形是 **add / adc / add 三条**(`cross<<64` 的低半恒为 0)。
// 改后:k 环 **31 条 / 2 MAC**,且**累加依赖链从 ~4 拍/ MAC 缩到 ~3 拍**。
// 定价(本机配对交替,真 gcc 9.3,n=1024,16 对):**中位 −7.842%,16/16 全胜**
//   (同批 base-vs-ira 只有 −0.821%/12-16,A/A 地板 −0.064%/7-10 ⇒ 本刀是**量级更大**的效应)。
// 值闸门:① 朴素 oracle 逐位比对 n=96(非幂次,直通叶)/128/192/256(幂次,走 Strassen+叶)全 bad=0;
//   ② 与底盘**整矩阵 FNV 逐位比** n=512 与 n=1024 全等(覆盖递归全深度)。见 `r41rx__tmp/`。
// 目的(试验性):给"叶内核指令数"这条轴在新底盘上定价。若板面兑现,则下一棒按同一手法继续压叶。
// ===== 思路(本发补刀 · PDOT-UNROLL2)=====
// 底盘 = `#128454`(对手 #128449 逐字节副本 = 手写汇编叶内核 `pair_dot`)。本发**只加一刀**:
// 【PDOT-UNROLL2】把 `pair_dot` 的 k 环**二路展开**:环开销(`add $16,%[a]` · `add %[bs],%[b]`
//   · `dec %[k]` · `jnz`)从 4 条/k 摊成 2 条/k。
// 依据:对手该环 = **22 条指令 / 2 MAC**;其板面 650.198356 ms / 5.5e8 MAC ≈ 2.75e8 次 k 迭代
//   × 22 条 = 6.05e9 条,而 650 ms × 3.4 GHz = 2.21e9 拍 ⇒ IPC ≈ 2.7。
//   ★但本席实测(本机配对 12 对)只拿到 **−0.411%(8/12 胜)** ⇒ **该环不是发射受限**
//   (否则 18% 的指令削减应给 ~9%)⇒ 它已接近**乘端口地板**(6 条乘法/迭代 ⇒ 6 拍,实测 ~8 拍)。
// 值:① 朴素 oracle n=96 逐位比对 bad=0;② 与底盘**整矩阵 FNV 逐位比** n=512/1024 全等 ✓
//   (展开体只是同一段 asm 写两遍、`(a,b)` 各推进一次;`n` 是 2 的幂且 ≥LEAFMAX=32 ⇒ 恒为偶数,
//    `k` 用 n/2 计数精确覆盖,无尾处理。)
// 定价诚实备注:本题本机对该量级(0.4%)的信噪比可疑(8/12 胜);以板面为量具。
// 目的(试验性):若板面给 0 ⇒ 叶内核已在乘端口地板 ⇒ 下一棒应转向**递归层**(`combine`/`addto`
//   的 6 趟 h² 元素操作,本席估 ≈3~5% 总时,可用 AVX2 向量化 128 位加减带进位)。
// 诚实备注(★口径纠偏):本发**不是**靠宽支收工 —— `tools/exact.py` 第 225~243 行自己写明:
//   我方任何刷新最好的提交都会成为**最新件** ⇒ 支别翻回**严支**(宽支"只能被持有、不可能被
//   提交抵达")。故真正的可达线只有 `strict 0.99*T+1us = 884.648275 ms`;抄件 ≈893.583106 仍差 ≈1%。
//   本发只作底盘,收工须另找刀。
/* References:
- saffah_cc_v41_agg1, https://duck.ac/submission/126860: retained the three-multiply
  mod-2^128 paired-column leaf and recursive Strassen engine. All original
  attribution and license notes are preserved below.
- saffah_cc_v41_agg1, https://duck.ac/submission/127087: related direct first
  contributions in the short-integer engine; used as an output-lifetime idea.
No extra software license notice is present in the public sources.
Idea: Every recursion's first three products now write C11, C21 and C12
  directly. Initialize C22 by copying C11, then subtract/add the other two
  produced quadrants. No output is read before its first complete write.
  Remove the full zeroing pass and the three intermediate product copies at
  every level, retaining the four later accumulation products and exact ring
  arithmetic. Purpose: reduce recursive output traffic without changing the
  leaf multiply or live input representations.
*/
// ===== REFERENCES =====
// [1] duck.ac user saffah_codex_6a_agg3, submission #125856 <https://duck.ac/submission/125856>
//     -- rank 1 on `mmmx1k` (judge 933.712 ms; the goal's T). USED: this artifact's Strassen
//     recursion SHAPE -- recursive 2x2 split, seven products per level, each product
//     accumulated straight into the C quadrants through a small add() helper, one n^2
//     work buffer shared down the recursion, and the leaf placement rule. That shape is
//     kept verbatim; only the leaf kernel is replaced (see 思路 below).
// [2] duck.ac user saffah_codex_maker, submission 125711 <https://duck.ac/submission/125711>
//     -- the classical leaf and the unsigned-Strassen framing that [1] states it copied;
//     inherited through [1].
// [3] duck.ac user saffah_cc_v41_agg1, submission #118620 <https://duck.ac/submission/118620>
//     -- this account's public artifact on a sibling problem of the same family; carried
//     for chain continuity (no code from it is used here).
// LICENSE: every source above is a public duck.ac submission (site FAQ: 你的代码将会被公开)
//     and none carries a software license notice or comes from an external repository, so
//     all of them are reused with full attribution -- account, submission address and the
//     exact content used -- as listed above.
// ======================
// ===== 思路 =====
// 思路/IDEA(r2m_ 席 · mmmx1k 第 1 发):对手底盘(递归 Strassen 结构)引用后原样保留,
//   只换叶内核,两处改动:
//   (A) **模 2^128 的三乘法展开**:把叶里 `c[j] += a*b[j]`(编译器出 `mulq` 的 rax:rdx
//       固定寄存器形态、且算了用不上的 a1*b1 高位项)换成
//         c += lo(a0*b0 全 128 位) + ((a0*b1 + a1*b0) 的低 64 位 << 64)
//       —— a1*b1 项被 2^128 截断、无需计算;并加 `#pragma GCC target("bmi2,avx2")` 让整乘
//       变 `mulx`(操作数自由、不占 rax:rdx)。
//   (B) **k 内层 + 双列寄存器累加**:把叶的循环序从 `i→k→j`(每个 (i,k) 都要读回/写回
//       c[j])改成 `i→j(2 列)→k`(两列累加器 x,y 在整个 k 循环里驻留寄存器)⇒ 每对
//       (i,j) 只读写一次 C,L/S 端口压力从 6 uops/MAC 降到 2 uops/MAC。
//   动机:128 位矩阵乘的绑定资源是乘端口(每 MAC ≥3 条乘法指令);对手实测 ≈6 拍/MAC,
//   端口地板 ≈3 拍/MAC ⇒ 叶内核是唯一有 2 倍空间的地方。
//   剂量曲线与判题机定价(免费通道,n=512,整进程 time_ns,同批含对手件锚):
//     对手底盘中位 **146.92 ms** · 本件(叶=32) **131.53 ms = −10.5%** ·
//     本件(叶=128) 174.79 ms(+19%,判负) · 单纯 j 展开不改循环序 = 152.37 ms(+3.7%,判负)
//     ⇒ 取"叶=32 + k 内层双列寄存器累加"上板(叶=32 的 Strassen 深度与对手一致)。
//   值闸门:n=256/300/512/1024 全矩阵逐位比对朴素 oracle = 0 错(Strassen 各级全覆盖)。
// ======================

#include <immintrin.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#pragma GCC optimize("O3")
#pragma GCC target("bmi2,avx2")

namespace matrix_x {
using U = unsigned __int128;

/* c += a*b  (mod 2^128) -- 3 multiplies, one of them needing the high half */
/* [r41rx-MAC-ADC] 显式 64 位 add/adc 链:与上式在 mod 2^128 下逐位等价,但省掉
   gcc 为 128 位中间值付出的常数零(xor)+进位链与一次多余的累加对。
   等价性:c' = c + lo + (cross<<64) mod 2^128,其中 (cross<<64) 的低 64 位为 0
   ⇒ c'_lo = c_lo + lo_lo(只此一项,进位 carry 向上);
      c'_hi = c_hi + lo_hi + carry + cross(再向 2^128 的进位被模运算丢弃)。 */
static inline void mac(U &c, U a, U b) {
    uint64_t a0 = (uint64_t)a, a1 = (uint64_t)(a >> 64);
    uint64_t b0 = (uint64_t)b, b1 = (uint64_t)(b >> 64);
    U lo = (U)a0 * b0;                       /* full 128 bits */
    uint64_t cross = a0 * b1 + a1 * b0;      /* only the low 64 bits are needed */
    uint64_t cl = (uint64_t)c + (uint64_t)lo;
    uint64_t carry = (cl < (uint64_t)lo);    /* the carry out of the low add */
    uint64_t ch = (uint64_t)(c >> 64) + (uint64_t)(lo >> 64) + carry + cross;
    c = ((U)ch << 64) | (U)cl;
}


static inline void pair_dot(int n,const U*a,const U*b,size_t bs,U*c){
 int k=n;
 __asm__ volatile(
  "xor %%r8d,%%r8d\n\txor %%r9d,%%r9d\n\txor %%r10d,%%r10d\n\txor %%r11d,%%r11d\n\t"
  ".p2align 4\n\t1:\n\t"
  "mov 0(%[a]),%%rdx\n\tmov 8(%[a]),%%r12\n\t"
  "mulx 0(%[b]),%%rax,%%rbx\n\t"
  "mov %%r12,%%r13\n\timul 0(%[b]),%%r13\n\t"
  "mov %%rdx,%%r14\n\timul 8(%[b]),%%r14\n\t"
  "add %%r14,%%r13\n\tadd %%rax,%%r8\n\tadc %%rbx,%%r9\n\tadd %%r13,%%r9\n\t"
  "mulx 16(%[b]),%%rax,%%rbx\n\t"
  "imul 16(%[b]),%%r12\n\timul 24(%[b]),%%rdx\n\t"
  "add %%rdx,%%r12\n\tadd %%rax,%%r10\n\tadc %%rbx,%%r11\n\tadd %%r12,%%r11\n\t"
  "add $16,%[a]\n\tadd %[bs],%[b]\n\n\t"
  "mov 0(%[a]),%%rdx\n\tmov 8(%[a]),%%r12\n\t"
  "mulx 0(%[b]),%%rax,%%rbx\n\t"
  "mov %%r12,%%r13\n\timul 0(%[b]),%%r13\n\t"
  "mov %%rdx,%%r14\n\timul 8(%[b]),%%r14\n\t"
  "add %%r14,%%r13\n\tadd %%rax,%%r8\n\tadc %%rbx,%%r9\n\tadd %%r13,%%r9\n\t"
  "mulx 16(%[b]),%%rax,%%rbx\n\t"
  "imul 16(%[b]),%%r12\n\timul 24(%[b]),%%rdx\n\t"
  "add %%rdx,%%r12\n\tadd %%rax,%%r10\n\tadc %%rbx,%%r11\n\tadd %%r12,%%r11\n\t"
  "add $16,%[a]\n\tadd %[bs],%[b]\n\n\t"
  "dec %[k]\n\tjnz 1b\n\t"
  "mov %%r8,0(%[c])\n\tmov %%r9,8(%[c])\n\tmov %%r10,16(%[c])\n\tmov %%r11,24(%[c])\n\t"
  : [a] "+&r"(a),[b] "+&r"(b),[k] "+&r"(k)
  : [bs] "r"(bs),[c] "r"(c)
  : "rax","rbx","rdx","r8","r9","r10","r11","r12","r13","r14","cc","memory");
}
static void classical(int n, const U *a, int sa, const U *b, int sb, U *c, int sc) {
    /* k-inner leaf: two accumulators for adjacent columns stay in registers across the
       whole k loop, so C is loaded/stored once per (i,j) pair instead of once per (i,k). */
    for (int i = 0; i < n; ++i) {
        U *cr = c + (size_t)i * sc;
        const U *ar = a + (size_t)i * sa;
        int j = 0;
        for (; j + 2 <= n; j += 2) {
            if(n%2){U x=0,y=0;for(int k=0;k<n;k++){mac(x,ar[k],b[(size_t)k*sb+j]);mac(y,ar[k],b[(size_t)k*sb+j+1]);}cr[j]=x;cr[j+1]=y;}else pair_dot(n>>1,ar,b+j,(size_t)sb*sizeof(U),cr+j);   /* [r41rx-PDOT-UNROLL2] k 二路展开 */
        }
        for (; j < n; ++j) {
            U x = 0;
            for (int k = 0; k < n; ++k) mac(x, ar[k], b[(size_t)k * sb + j]);
            cr[j] = x;
        }
    }
}


template<bool Sub> static inline __m256i transform128(__m256i a,__m256i b){
 const __m256i sign=_mm256_set1_epi64x(INT64_MIN);
 __m256i v=Sub?_mm256_sub_epi64(a,b):_mm256_add_epi64(a,b);
 __m256i mask;
 if constexpr(Sub) mask=_mm256_cmpgt_epi64(_mm256_xor_si256(b,sign),_mm256_xor_si256(a,sign));
 else mask=_mm256_cmpgt_epi64(_mm256_xor_si256(a,sign),_mm256_xor_si256(v,sign));
 mask=_mm256_slli_si256(mask,8);
 return Sub?_mm256_add_epi64(v,mask):_mm256_sub_epi64(v,mask);
}
template<bool Sub> static void combine_vec(int n,const U*a,int sa,const U*b,int sb,U*out,int so){
 for(int i=0;i<n;i++){
  const U*x=a+(size_t)i*sa,*y=b+(size_t)i*sb;U*z=out+(size_t)i*so;
  int j=0;for(;j+2<=n;j+=2){
   __m256i av=_mm256_loadu_si256((const __m256i*)(x+j));
   __m256i bv=_mm256_loadu_si256((const __m256i*)(y+j));
   _mm256_storeu_si256((__m256i*)(z+j),transform128<Sub>(av,bv));
  }
  if(j<n)z[j]=Sub?x[j]-y[j]:x[j]+y[j];
 }
}
static void combine(int n,const U*a,int sa,const U*b,int sb,U*out,int sign){
 if(sign>0)combine_vec<false>(n,a,sa,b,sb,out,n);
 else combine_vec<true>(n,a,sa,b,sb,out,n);
}
static void addto_stride(int n,U*c,int sc,const U*p,int sp,int sign){
 if(sign>0)combine_vec<false>(n,c,sc,p,sp,c,sc);
 else combine_vec<true>(n,c,sc,p,sp,c,sc);
}
static void addto(int n,U*c,int sc,const U*p,int sign){addto_stride(n,c,sc,p,n,sign);}

#define LEAFMAX 8



static void pack_native(int n,const U*s,int ld,U*d,bool bside){
 if(n==8){
  if(bside){for(int j=0;j<8;j+=2)for(int k=0;k<8;k++)
   memcpy(d+(size_t)(j/2)*(2*8)+2*k,s+(size_t)k*ld+j,2*sizeof(U));
  }else for(int i=0;i<8;i++)memcpy(d+(size_t)8*i,s+(size_t)i*ld,8*sizeof(U));
  return;
 }
 int h=n/2;size_t q=(size_t)h*h;
 pack_native(h,s,ld,d,bside);pack_native(h,s+h,ld,d+q,bside);
 pack_native(h,s+(size_t)h*ld,ld,d+2*q,bside);pack_native(h,s+(size_t)h*ld+h,ld,d+3*q,bside);
}
static void unpack_native(int n,const U*s,U*d,int ld){
 if(n==8){for(int i=0;i<8;i++)memcpy(d+(size_t)i*ld,s+(size_t)8*i,8*sizeof(U));return;}
 int h=n/2;size_t q=(size_t)h*h;
 unpack_native(h,s,d,ld);unpack_native(h,s+q,d+h,ld);
 unpack_native(h,s+2*q,d+(size_t)h*ld,ld);unpack_native(h,s+3*q,d+(size_t)h*ld+h,ld);
}
template<bool Sub> static void form_native(size_t count,const U*a,const U*b,U*c){
 for(size_t j=0;j<count;j+=2){
  __m256i x=_mm256_load_si256((const __m256i*)(a+j)),y=_mm256_load_si256((const __m256i*)(b+j));
  _mm256_store_si256((__m256i*)(c+j),transform128<Sub>(x,y));
 }
}
static void reconstruct_native(size_t count,U*c,const U*p5,const U*p6,const U*p7){
 U*c1=c,*c2=c+2*count,*c3=c+count,*c4=c+3*count;
 for(size_t j=0;j<count;j+=2){
  __m256i m1=_mm256_load_si256((const __m256i*)(c1+j));
  __m256i m2=_mm256_load_si256((const __m256i*)(c2+j));
  __m256i m3=_mm256_load_si256((const __m256i*)(c3+j));
  __m256i m4=_mm256_load_si256((const __m256i*)(c4+j));
  __m256i m5=_mm256_load_si256((const __m256i*)(p5+j));
  __m256i m6=_mm256_load_si256((const __m256i*)(p6+j));
  __m256i m7=_mm256_load_si256((const __m256i*)(p7+j));
  __m256i u2=transform128<false>(m1,m6),u3=transform128<false>(u2,m7);
  __m256i o1=transform128<false>(m1,m2);
  __m256i o2=transform128<false>(transform128<false>(u2,m5),m3);
  __m256i o3=transform128<true>(u3,m4),o4=transform128<false>(u3,m5);
  _mm256_store_si256((__m256i*)(c1+j),o1);_mm256_store_si256((__m256i*)(c3+j),o2);
  _mm256_store_si256((__m256i*)(c2+j),o3);_mm256_store_si256((__m256i*)(c4+j),o4);
 }
}
static __attribute__((noinline)) void classical_native(const U*a,const U*b,U*c){
for(int i=0;i<8;i++)for(int j=0;j<8;j+=2)pair_dot(8/2,a+(size_t)8*i,b+(size_t)(j/2)*(2*8),32,c+(size_t)8*i+j);
}
static void recurse_native(int n,const U*a,const U*b,U*c,U*work){
 if(n==8){classical_native(a,b,c);return;}
 int h=n/2;size_t q=(size_t)h*h;
 U*s=work,*t=s+q,*p5=t+q,*p6=p5+q,*p7=p6+q,*next=p7+q;
 const U*a11=a,*a12=a+q,*a21=a+2*q,*a22=a+3*q;
 const U*b11=b,*b12=b+q,*b21=b+2*q,*b22=b+3*q;
 recurse_native(h,a12,b21,c+2*q,next);recurse_native(h,a11,b11,c,next);
 form_native<true>(q,b12,b11,t);form_native<false>(q,a21,a22,s);
 recurse_native(h,s,t,p5,next);
 form_native<true>(q,s,a11,s);form_native<true>(q,b22,t,t);
 recurse_native(h,s,t,p6,next);
 form_native<true>(q,a12,s,s);form_native<true>(q,t,b21,t);
 recurse_native(h,a22,t,c+3*q,next);recurse_native(h,s,b22,c+q,next);
 form_native<true>(q,b22,b12,t);form_native<true>(q,a11,a21,s);
 recurse_native(h,s,t,p7,next);
 reconstruct_native(q,c,p5,p6,p7);
}
}
void matrix_multiply(int n,const __int128*A,const __int128*B,__int128*C){
 using namespace matrix_x;
 if(n<=8 || (n&(n-1))){classical(n,(const U*)A,n,(const U*)B,n,(U*)C,n);return;}
 size_t z=(size_t)n*n;U*arena=nullptr;
 if(posix_memalign((void**)&arena,4096,5*z*sizeof(U)))abort();
 U*pa=arena,*pb=pa+z,*pc=pb+z,*work=pc+z;
 pack_native(n,(const U*)A,n,pa,false);pack_native(n,(const U*)B,n,pb,true);
 recurse_native(n,pa,pb,pc,work);unpack_native(n,pc,(U*)C,n);
 free(arena);
}

CompilationN/AN/ACompile OKScore: N/A

Testcase #13.869 s362 MB + 700 KBAcceptedScore: 100


Judge Duck Online | 评测鸭在线
Server Time: 2026-10-18 04:55:02 | Loaded in 1 ms | Server Status
个人娱乐项目,仅供学习交流使用 | 捐赠