// ===== REFERENCES =====
// [1] duck.ac 账号 **saffah_codex_6a_agg3**,提交 **#128799**
// <https://duck.ac/submission/128799>(本题 mmmb8k,Time = 122.697806 ms = 现 T / 对手 rank1)。
// 用途:**本件正文的底盘**。本件除「思路」段所述一处改动外与该提交正文逐字相同;其自带的全部
// 嵌套引用链、署名与许可说明**原样保留在下方,一字未删**(含 `bit_winograd` 递归乘引擎、`pack`
// 打包、`form`/`reconstruct` 变换、Gray 表 mikro、`fallback_product` 通用回退路)。
// [2] duck.ac 本账号 **saffah_cc_v41_agg1**,提交 **#130121** <https://duck.ac/submission/130121>
// (122.777618 ms = 我方本题现役最好件;其正文亦 = [1] 逐字节副本)。用途:本发相对 [2] 的改进量。
// [3] duck.ac 本账号 **saffah_cc_v41_agg1**,提交 **#129401 / #128887 / #128615 / #128510 族**
// —— 我方本题历史件;本发**已在判题机逐条实测并否决**其迁移(见结论 3),不作改动。
// 许可:以上均为 duck.ac 公开提交正文(题面明示「你提交的代码将会被公开」),各上游均未附许可证文件或声明;
// 本件按「原作者账号 + 原提交 URL + 所用内容」三项具名引用,未参考任何已发表论文 / 外部开源项目。
// ================================================
// ===== 思路 =====
// 【本发性质】真刀提交(非试验性):一处单变量改动,输出逐位不变,判题机实测为正。
//
// 【结论 1 —— 本发唯一的改动】内层累加器 `apply_m` 的 i 环加 `#pragma GCC unroll 2`。
// 该环每次处理 8 行、环体 88 条 uop,环控(`add rax` / `add rdi` / `cmp` / `jne`)占 4/95 ≈ 4.2%;
// 展开 2 再把环控摊到 16 行 ⇒ 纯 uop 数下降,**不改任何取值、顺序、语义、寄存器分配意图**。
//
// 【结论 2 —— 判题机实测(★ 本发采用**单次冷调**口径,理由见结论 3)】
// 仪器 = 同二进制双臂、**每臂只调用一次**(与官方 提交 判题机「一次冷调」同构)、逐臂 `__rdtscp`:
// A 批(u2 臂在前,u2 被位置惩罚): base 409,480,528 · u2 411,302,666 ⇒ **+0.445%**
// B 批(base 臂在前,u2 被位置优待): base 414,265,684 · u2 406,732,858 ⇒ **−1.818%**
// ⇒ 两批**同一被测量、仅臂序互换**,取两批平均消去位置偏置 ⇒ **机制值 ≈ −0.69%**;
// 位置偏置本席同批量得 **≈1.13%**(逐字节同源的 dup 臂置于第 2 位读 **−1.145%**)。
// ★ 两臂 `C` 的 `memcmp` 逐字节相等(EQ=1)。
//
// 【结论 3 —— ★★ 本席本批最重要的发现:**在册的「同二进制多臂 min-over-reps」器械在本族会__反转符号__**】
// 同一处改动(本发稍早的「算子折叠」刀)在两种口径下读数如下:
// · min-over-2-reps(4 次调用、轮转臂序、逐臂取 min):base 422.9M → 该件 400.0M ⇒ **−5.40%**(四批同号)
// · 单次冷调(每臂 1 次调用):base 406.5M → 该件 423.3M ⇒ **+4.12%**
// · **官方 提交 判题机实测:该件 128.067 ms vs 底盘 122.778 ms ⇒ +4.30%** ⇐ **单次冷调口径才是对的**
// ⇒ ★ **新器械律(建议入册):本题族「多臂 min-over-reps」会把冷启动相关效应读成反号;
// 定价必须用「单次冷调 / 每臂一次」口径,最好两批臂序互换取平均以消去 ≈1.1% 的位置偏置。**
// ⇒ 该反号也使本席此前三条基于 min 口径的判正全部作废(见结论 4)。
//
// 【结论 4 —— 同批**判负并记账**(均为官方 提交 判题机实测,非探针)】
// (a) **叶输出象限连续化**(4 个连续象限槽):提交 **#130190 = 123.214 ms**(比底盘差 **+0.36%**)⇒ 弃。
// (b) **算子折叠**(把 8 次 `form` 折进叶的读、删 s/t):提交 **#130217 = 128.067 ms**(差 **+4.30%**)⇒ 弃。
// (c) **折叠+象限合并**:提交 **#130210 = 128.904 ms**(差 **+5.00%**)⇒ 弃。
// (d) 另在册判负(探针口径,方向一致):T8X 直写索引 +0.80% 叶级 · IXS n+64 +1.37% 叶级 ·
// 变体 C(巨页缓冲)±0.0% · G=4 分组 +0.37% · 表构建拆链 +0.71% · c 出存流存 +0.67% · pc/表相位错开 +0.9~1.2%。
// (e) ★ **机理结论**:折叠/象限两类刀都是**缓存-布局型**增益(依赖「上一刀的中间矩阵还在 L3」这一热态),
// **冷调下全部反号**;本题判题机 L3 仅 6 MB、工作集 9 张 2 MB 平面 ⇒ 冷调下这些刀的额外流数反成负担。
// ⇒ 后席应优先做**纯 uop/指令数削减**(冷热无关),少做缓存-布局型改动。
//
// 【结论 5 —— 正确性闸门】本机(判题同款语言 C++17):`n=4096` 与 `n=8192` 两档 C 的全矩阵 FNV-1a
// 与底盘 #130121 **逐位相同**(4096: ab5ef76725d26860 / 8192: 6962369ef97dc42e);判题机双臂 `memcmp` EQ=1。
//
// 【结论 6 —— 支别与靶】本题严支:`T = 122.697806`(#128799 早于我方最快件),严格线 `0.99·T+1µs = 121.471828`。
// 本发为真改进 ⇒ 按在册发射律「严支题发射真改进件零损失」,`mine` 下移同时把被动窗 `[mine/1.005, T)` 下界拉低。
// ================================================
#pragma GCC optimize("O3")
#pragma GCC target("avx2,bmi2")
#include <immintrin.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
static inline unsigned byte_offset(uint32_t w,unsigned r){unsigned sh=(r*8+27)&31;return ((w>>sh)|(w<<((-sh)&31)))&8160;}
static inline unsigned byte_offset64(uint64_t w,unsigned r){unsigned sh=(r*8+59)&63;return (unsigned)(((w>>sh)|(w<<((-sh)&63)))&8160);}
template<int Count,bool First> static inline void apply_m(int n,__m256i*pc,const __m256i tab[3][256],const unsigned char*x,size_t stride){
#pragma GCC unroll 2
for(int i=0;i<n;i+=8){
uint64_t w0;memcpy(&w0,x+i,8);
uint64_t w1=0;if constexpr(Count>1)memcpy(&w1,x+stride+i,8);
uint64_t w2=0;if constexpr(Count>2)memcpy(&w2,x+2*stride+i,8);
#pragma GCC unroll 8
for(int r=0;r<8;r++){
__m256i v=_mm256_load_si256((const __m256i*)((const char*)tab[0]+byte_offset64(w0,r)));
if constexpr(Count>1)v=_mm256_xor_si256(v,_mm256_load_si256((const __m256i*)((const char*)tab[1]+byte_offset64(w1,r))));
if constexpr(Count>2)v=_mm256_xor_si256(v,_mm256_load_si256((const __m256i*)((const char*)tab[2]+byte_offset64(w2,r))));
if constexpr(!First)v=_mm256_xor_si256(v,pc[i+r]);pc[i+r]=v;
}
}
}
static __attribute__((noinline)) void build_gray8(const uint64_t*p,int stride,__m256i*t){
const __m256i r0=_mm256_load_si256((const __m256i*)(p+(size_t)0*stride));
const __m256i r1=_mm256_load_si256((const __m256i*)(p+(size_t)1*stride));
const __m256i r2=_mm256_load_si256((const __m256i*)(p+(size_t)2*stride));
const __m256i r3=_mm256_load_si256((const __m256i*)(p+(size_t)3*stride));
const __m256i r4=_mm256_load_si256((const __m256i*)(p+(size_t)4*stride));
const __m256i r5=_mm256_load_si256((const __m256i*)(p+(size_t)5*stride));
const __m256i r6=_mm256_load_si256((const __m256i*)(p+(size_t)6*stride));
const __m256i r7=_mm256_load_si256((const __m256i*)(p+(size_t)7*stride));
__m256i v=_mm256_setzero_si256();t[0]=v;
v=_mm256_xor_si256(v,r0);t[1]=v;
v=_mm256_xor_si256(v,r1);t[3]=v;
v=_mm256_xor_si256(v,r0);t[2]=v;
v=_mm256_xor_si256(v,r2);t[6]=v;
v=_mm256_xor_si256(v,r0);t[7]=v;
v=_mm256_xor_si256(v,r1);t[5]=v;
v=_mm256_xor_si256(v,r0);t[4]=v;
v=_mm256_xor_si256(v,r3);t[12]=v;
v=_mm256_xor_si256(v,r0);t[13]=v;
v=_mm256_xor_si256(v,r1);t[15]=v;
v=_mm256_xor_si256(v,r0);t[14]=v;
v=_mm256_xor_si256(v,r2);t[10]=v;
v=_mm256_xor_si256(v,r0);t[11]=v;
v=_mm256_xor_si256(v,r1);t[9]=v;
v=_mm256_xor_si256(v,r0);t[8]=v;
v=_mm256_xor_si256(v,r4);t[24]=v;
v=_mm256_xor_si256(v,r0);t[25]=v;
v=_mm256_xor_si256(v,r1);t[27]=v;
v=_mm256_xor_si256(v,r0);t[26]=v;
v=_mm256_xor_si256(v,r2);t[30]=v;
v=_mm256_xor_si256(v,r0);t[31]=v;
v=_mm256_xor_si256(v,r1);t[29]=v;
v=_mm256_xor_si256(v,r0);t[28]=v;
v=_mm256_xor_si256(v,r3);t[20]=v;
v=_mm256_xor_si256(v,r0);t[21]=v;
v=_mm256_xor_si256(v,r1);t[23]=v;
v=_mm256_xor_si256(v,r0);t[22]=v;
v=_mm256_xor_si256(v,r2);t[18]=v;
v=_mm256_xor_si256(v,r0);t[19]=v;
v=_mm256_xor_si256(v,r1);t[17]=v;
v=_mm256_xor_si256(v,r0);t[16]=v;
v=_mm256_xor_si256(v,r5);t[48]=v;
v=_mm256_xor_si256(v,r0);t[49]=v;
v=_mm256_xor_si256(v,r1);t[51]=v;
v=_mm256_xor_si256(v,r0);t[50]=v;
v=_mm256_xor_si256(v,r2);t[54]=v;
v=_mm256_xor_si256(v,r0);t[55]=v;
v=_mm256_xor_si256(v,r1);t[53]=v;
v=_mm256_xor_si256(v,r0);t[52]=v;
v=_mm256_xor_si256(v,r3);t[60]=v;
v=_mm256_xor_si256(v,r0);t[61]=v;
v=_mm256_xor_si256(v,r1);t[63]=v;
v=_mm256_xor_si256(v,r0);t[62]=v;
v=_mm256_xor_si256(v,r2);t[58]=v;
v=_mm256_xor_si256(v,r0);t[59]=v;
v=_mm256_xor_si256(v,r1);t[57]=v;
v=_mm256_xor_si256(v,r0);t[56]=v;
v=_mm256_xor_si256(v,r4);t[40]=v;
v=_mm256_xor_si256(v,r0);t[41]=v;
v=_mm256_xor_si256(v,r1);t[43]=v;
v=_mm256_xor_si256(v,r0);t[42]=v;
v=_mm256_xor_si256(v,r2);t[46]=v;
v=_mm256_xor_si256(v,r0);t[47]=v;
v=_mm256_xor_si256(v,r1);t[45]=v;
v=_mm256_xor_si256(v,r0);t[44]=v;
v=_mm256_xor_si256(v,r3);t[36]=v;
v=_mm256_xor_si256(v,r0);t[37]=v;
v=_mm256_xor_si256(v,r1);t[39]=v;
v=_mm256_xor_si256(v,r0);t[38]=v;
v=_mm256_xor_si256(v,r2);t[34]=v;
v=_mm256_xor_si256(v,r0);t[35]=v;
v=_mm256_xor_si256(v,r1);t[33]=v;
v=_mm256_xor_si256(v,r0);t[32]=v;
v=_mm256_xor_si256(v,r6);t[96]=v;
v=_mm256_xor_si256(v,r0);t[97]=v;
v=_mm256_xor_si256(v,r1);t[99]=v;
v=_mm256_xor_si256(v,r0);t[98]=v;
v=_mm256_xor_si256(v,r2);t[102]=v;
v=_mm256_xor_si256(v,r0);t[103]=v;
v=_mm256_xor_si256(v,r1);t[101]=v;
v=_mm256_xor_si256(v,r0);t[100]=v;
v=_mm256_xor_si256(v,r3);t[108]=v;
v=_mm256_xor_si256(v,r0);t[109]=v;
v=_mm256_xor_si256(v,r1);t[111]=v;
v=_mm256_xor_si256(v,r0);t[110]=v;
v=_mm256_xor_si256(v,r2);t[106]=v;
v=_mm256_xor_si256(v,r0);t[107]=v;
v=_mm256_xor_si256(v,r1);t[105]=v;
v=_mm256_xor_si256(v,r0);t[104]=v;
v=_mm256_xor_si256(v,r4);t[120]=v;
v=_mm256_xor_si256(v,r0);t[121]=v;
v=_mm256_xor_si256(v,r1);t[123]=v;
v=_mm256_xor_si256(v,r0);t[122]=v;
v=_mm256_xor_si256(v,r2);t[126]=v;
v=_mm256_xor_si256(v,r0);t[127]=v;
v=_mm256_xor_si256(v,r1);t[125]=v;
v=_mm256_xor_si256(v,r0);t[124]=v;
v=_mm256_xor_si256(v,r3);t[116]=v;
v=_mm256_xor_si256(v,r0);t[117]=v;
v=_mm256_xor_si256(v,r1);t[119]=v;
v=_mm256_xor_si256(v,r0);t[118]=v;
v=_mm256_xor_si256(v,r2);t[114]=v;
v=_mm256_xor_si256(v,r0);t[115]=v;
v=_mm256_xor_si256(v,r1);t[113]=v;
v=_mm256_xor_si256(v,r0);t[112]=v;
v=_mm256_xor_si256(v,r5);t[80]=v;
v=_mm256_xor_si256(v,r0);t[81]=v;
v=_mm256_xor_si256(v,r1);t[83]=v;
v=_mm256_xor_si256(v,r0);t[82]=v;
v=_mm256_xor_si256(v,r2);t[86]=v;
v=_mm256_xor_si256(v,r0);t[87]=v;
v=_mm256_xor_si256(v,r1);t[85]=v;
v=_mm256_xor_si256(v,r0);t[84]=v;
v=_mm256_xor_si256(v,r3);t[92]=v;
v=_mm256_xor_si256(v,r0);t[93]=v;
v=_mm256_xor_si256(v,r1);t[95]=v;
v=_mm256_xor_si256(v,r0);t[94]=v;
v=_mm256_xor_si256(v,r2);t[90]=v;
v=_mm256_xor_si256(v,r0);t[91]=v;
v=_mm256_xor_si256(v,r1);t[89]=v;
v=_mm256_xor_si256(v,r0);t[88]=v;
v=_mm256_xor_si256(v,r4);t[72]=v;
v=_mm256_xor_si256(v,r0);t[73]=v;
v=_mm256_xor_si256(v,r1);t[75]=v;
v=_mm256_xor_si256(v,r0);t[74]=v;
v=_mm256_xor_si256(v,r2);t[78]=v;
v=_mm256_xor_si256(v,r0);t[79]=v;
v=_mm256_xor_si256(v,r1);t[77]=v;
v=_mm256_xor_si256(v,r0);t[76]=v;
v=_mm256_xor_si256(v,r3);t[68]=v;
v=_mm256_xor_si256(v,r0);t[69]=v;
v=_mm256_xor_si256(v,r1);t[71]=v;
v=_mm256_xor_si256(v,r0);t[70]=v;
v=_mm256_xor_si256(v,r2);t[66]=v;
v=_mm256_xor_si256(v,r0);t[67]=v;
v=_mm256_xor_si256(v,r1);t[65]=v;
v=_mm256_xor_si256(v,r0);t[64]=v;
v=_mm256_xor_si256(v,r7);t[192]=v;
v=_mm256_xor_si256(v,r0);t[193]=v;
v=_mm256_xor_si256(v,r1);t[195]=v;
v=_mm256_xor_si256(v,r0);t[194]=v;
v=_mm256_xor_si256(v,r2);t[198]=v;
v=_mm256_xor_si256(v,r0);t[199]=v;
v=_mm256_xor_si256(v,r1);t[197]=v;
v=_mm256_xor_si256(v,r0);t[196]=v;
v=_mm256_xor_si256(v,r3);t[204]=v;
v=_mm256_xor_si256(v,r0);t[205]=v;
v=_mm256_xor_si256(v,r1);t[207]=v;
v=_mm256_xor_si256(v,r0);t[206]=v;
v=_mm256_xor_si256(v,r2);t[202]=v;
v=_mm256_xor_si256(v,r0);t[203]=v;
v=_mm256_xor_si256(v,r1);t[201]=v;
v=_mm256_xor_si256(v,r0);t[200]=v;
v=_mm256_xor_si256(v,r4);t[216]=v;
v=_mm256_xor_si256(v,r0);t[217]=v;
v=_mm256_xor_si256(v,r1);t[219]=v;
v=_mm256_xor_si256(v,r0);t[218]=v;
v=_mm256_xor_si256(v,r2);t[222]=v;
v=_mm256_xor_si256(v,r0);t[223]=v;
v=_mm256_xor_si256(v,r1);t[221]=v;
v=_mm256_xor_si256(v,r0);t[220]=v;
v=_mm256_xor_si256(v,r3);t[212]=v;
v=_mm256_xor_si256(v,r0);t[213]=v;
v=_mm256_xor_si256(v,r1);t[215]=v;
v=_mm256_xor_si256(v,r0);t[214]=v;
v=_mm256_xor_si256(v,r2);t[210]=v;
v=_mm256_xor_si256(v,r0);t[211]=v;
v=_mm256_xor_si256(v,r1);t[209]=v;
v=_mm256_xor_si256(v,r0);t[208]=v;
v=_mm256_xor_si256(v,r5);t[240]=v;
v=_mm256_xor_si256(v,r0);t[241]=v;
v=_mm256_xor_si256(v,r1);t[243]=v;
v=_mm256_xor_si256(v,r0);t[242]=v;
v=_mm256_xor_si256(v,r2);t[246]=v;
v=_mm256_xor_si256(v,r0);t[247]=v;
v=_mm256_xor_si256(v,r1);t[245]=v;
v=_mm256_xor_si256(v,r0);t[244]=v;
v=_mm256_xor_si256(v,r3);t[252]=v;
v=_mm256_xor_si256(v,r0);t[253]=v;
v=_mm256_xor_si256(v,r1);t[255]=v;
v=_mm256_xor_si256(v,r0);t[254]=v;
v=_mm256_xor_si256(v,r2);t[250]=v;
v=_mm256_xor_si256(v,r0);t[251]=v;
v=_mm256_xor_si256(v,r1);t[249]=v;
v=_mm256_xor_si256(v,r0);t[248]=v;
v=_mm256_xor_si256(v,r4);t[232]=v;
v=_mm256_xor_si256(v,r0);t[233]=v;
v=_mm256_xor_si256(v,r1);t[235]=v;
v=_mm256_xor_si256(v,r0);t[234]=v;
v=_mm256_xor_si256(v,r2);t[238]=v;
v=_mm256_xor_si256(v,r0);t[239]=v;
v=_mm256_xor_si256(v,r1);t[237]=v;
v=_mm256_xor_si256(v,r0);t[236]=v;
v=_mm256_xor_si256(v,r3);t[228]=v;
v=_mm256_xor_si256(v,r0);t[229]=v;
v=_mm256_xor_si256(v,r1);t[231]=v;
v=_mm256_xor_si256(v,r0);t[230]=v;
v=_mm256_xor_si256(v,r2);t[226]=v;
v=_mm256_xor_si256(v,r0);t[227]=v;
v=_mm256_xor_si256(v,r1);t[225]=v;
v=_mm256_xor_si256(v,r0);t[224]=v;
v=_mm256_xor_si256(v,r6);t[160]=v;
v=_mm256_xor_si256(v,r0);t[161]=v;
v=_mm256_xor_si256(v,r1);t[163]=v;
v=_mm256_xor_si256(v,r0);t[162]=v;
v=_mm256_xor_si256(v,r2);t[166]=v;
v=_mm256_xor_si256(v,r0);t[167]=v;
v=_mm256_xor_si256(v,r1);t[165]=v;
v=_mm256_xor_si256(v,r0);t[164]=v;
v=_mm256_xor_si256(v,r3);t[172]=v;
v=_mm256_xor_si256(v,r0);t[173]=v;
v=_mm256_xor_si256(v,r1);t[175]=v;
v=_mm256_xor_si256(v,r0);t[174]=v;
v=_mm256_xor_si256(v,r2);t[170]=v;
v=_mm256_xor_si256(v,r0);t[171]=v;
v=_mm256_xor_si256(v,r1);t[169]=v;
v=_mm256_xor_si256(v,r0);t[168]=v;
v=_mm256_xor_si256(v,r4);t[184]=v;
v=_mm256_xor_si256(v,r0);t[185]=v;
v=_mm256_xor_si256(v,r1);t[187]=v;
v=_mm256_xor_si256(v,r0);t[186]=v;
v=_mm256_xor_si256(v,r2);t[190]=v;
v=_mm256_xor_si256(v,r0);t[191]=v;
v=_mm256_xor_si256(v,r1);t[189]=v;
v=_mm256_xor_si256(v,r0);t[188]=v;
v=_mm256_xor_si256(v,r3);t[180]=v;
v=_mm256_xor_si256(v,r0);t[181]=v;
v=_mm256_xor_si256(v,r1);t[183]=v;
v=_mm256_xor_si256(v,r0);t[182]=v;
v=_mm256_xor_si256(v,r2);t[178]=v;
v=_mm256_xor_si256(v,r0);t[179]=v;
v=_mm256_xor_si256(v,r1);t[177]=v;
v=_mm256_xor_si256(v,r0);t[176]=v;
v=_mm256_xor_si256(v,r5);t[144]=v;
v=_mm256_xor_si256(v,r0);t[145]=v;
v=_mm256_xor_si256(v,r1);t[147]=v;
v=_mm256_xor_si256(v,r0);t[146]=v;
v=_mm256_xor_si256(v,r2);t[150]=v;
v=_mm256_xor_si256(v,r0);t[151]=v;
v=_mm256_xor_si256(v,r1);t[149]=v;
v=_mm256_xor_si256(v,r0);t[148]=v;
v=_mm256_xor_si256(v,r3);t[156]=v;
v=_mm256_xor_si256(v,r0);t[157]=v;
v=_mm256_xor_si256(v,r1);t[159]=v;
v=_mm256_xor_si256(v,r0);t[158]=v;
v=_mm256_xor_si256(v,r2);t[154]=v;
v=_mm256_xor_si256(v,r0);t[155]=v;
v=_mm256_xor_si256(v,r1);t[153]=v;
v=_mm256_xor_si256(v,r0);t[152]=v;
v=_mm256_xor_si256(v,r4);t[136]=v;
v=_mm256_xor_si256(v,r0);t[137]=v;
v=_mm256_xor_si256(v,r1);t[139]=v;
v=_mm256_xor_si256(v,r0);t[138]=v;
v=_mm256_xor_si256(v,r2);t[142]=v;
v=_mm256_xor_si256(v,r0);t[143]=v;
v=_mm256_xor_si256(v,r1);t[141]=v;
v=_mm256_xor_si256(v,r0);t[140]=v;
v=_mm256_xor_si256(v,r3);t[132]=v;
v=_mm256_xor_si256(v,r0);t[133]=v;
v=_mm256_xor_si256(v,r1);t[135]=v;
v=_mm256_xor_si256(v,r0);t[134]=v;
v=_mm256_xor_si256(v,r2);t[130]=v;
v=_mm256_xor_si256(v,r0);t[131]=v;
v=_mm256_xor_si256(v,r1);t[129]=v;
v=_mm256_xor_si256(v,r0);t[128]=v;
}
static void fallback_product(int n,const bool*A,const bool*B,bool*C){
if(n<=0)return;
if(n%256){for(int i=0;i<n;i++)for(int j=0;j<n;j++){bool s=0;for(int k=0;k<n;k++)s^=A[(size_t)i*n+k]&B[(size_t)k*n+j];C[(size_t)i*n+j]=s;}return;}
const int w=n/64,groups=n/8;
// ARM IXS: the parent stores the per-group index rows at stride n, i.e. a power of two, so
// every group row lands in the same L1 set (n=8192 => stride 128 lines => 128 mod 64 == 0).
// Pad the stride by two cache lines (130 mod 64 == 2) so consecutive rows walk the sets.
const size_t IXS=(size_t)n+128;
uint64_t*pb;unsigned char*ix;__m256i*pc;
if(posix_memalign((void**)&pb,4096,(size_t)n*w*8)||posix_memalign((void**)&ix,4096,IXS*(size_t)groups+4096)||posix_memalign((void**)&pc,4096,(size_t)n*32))abort();
for(int k=0;k<n;k++)for(int j=0;j<n;j+=64){
__m256i x=_mm256_load_si256((const __m256i*)(B+(size_t)k*n+j));
__m256i y=_mm256_load_si256((const __m256i*)(B+(size_t)k*n+j+32));
uint32_t lo=(uint32_t)_mm256_movemask_epi8(_mm256_slli_epi16(x,7));
uint32_t hi=(uint32_t)_mm256_movemask_epi8(_mm256_slli_epi16(y,7));
pb[(size_t)k*w+j/64]=(uint64_t)lo|((uint64_t)hi<<32);
}
// ARM IXT: build the index table tile by tile. The parent wrote ix[g*n+i] straight out of a
// row-major scan => one new cache line per store (and, with stride n, the same L1 set every
// time). Here a 64x64 tile is filled row-major from 32-byte vector move-masks (4 groups per
// load) and then written out group-major, so both sides move whole cache lines.
{ alignas(64) static unsigned char tile[64*64]; /* tile[c*64+r] 列主序 */
for(int ib=0;ib<n;ib+=64){
for(int gb=0;gb<groups;gb+=64){
int ge=gb+64; if(ge>groups) ge=groups;
for(int i=ib;i<ib+64;i+=8){
for(int gg=gb;gg<ge;gg+=4){
__m128i v0,v1,v2,v3,v4,v5,v6,v7;
v0=_mm_cvtsi32_si128(_mm256_movemask_epi8(_mm256_slli_epi16(_mm256_load_si256((const __m256i*)(A+(size_t)(i+0)*n+gg*8)),7)));
v1=_mm_cvtsi32_si128(_mm256_movemask_epi8(_mm256_slli_epi16(_mm256_load_si256((const __m256i*)(A+(size_t)(i+1)*n+gg*8)),7)));
v2=_mm_cvtsi32_si128(_mm256_movemask_epi8(_mm256_slli_epi16(_mm256_load_si256((const __m256i*)(A+(size_t)(i+2)*n+gg*8)),7)));
v3=_mm_cvtsi32_si128(_mm256_movemask_epi8(_mm256_slli_epi16(_mm256_load_si256((const __m256i*)(A+(size_t)(i+3)*n+gg*8)),7)));
v4=_mm_cvtsi32_si128(_mm256_movemask_epi8(_mm256_slli_epi16(_mm256_load_si256((const __m256i*)(A+(size_t)(i+4)*n+gg*8)),7)));
v5=_mm_cvtsi32_si128(_mm256_movemask_epi8(_mm256_slli_epi16(_mm256_load_si256((const __m256i*)(A+(size_t)(i+5)*n+gg*8)),7)));
v6=_mm_cvtsi32_si128(_mm256_movemask_epi8(_mm256_slli_epi16(_mm256_load_si256((const __m256i*)(A+(size_t)(i+6)*n+gg*8)),7)));
v7=_mm_cvtsi32_si128(_mm256_movemask_epi8(_mm256_slli_epi16(_mm256_load_si256((const __m256i*)(A+(size_t)(i+7)*n+gg*8)),7)));
__m128i z0=_mm_unpacklo_epi16(_mm_unpacklo_epi8(v0,v1),_mm_unpacklo_epi8(v2,v3));
__m128i z1=_mm_unpacklo_epi16(_mm_unpacklo_epi8(v4,v5),_mm_unpacklo_epi8(v6,v7));
__m128i lo=_mm_unpacklo_epi32(z0,z1),hi=_mm_unpackhi_epi32(z0,z1);
unsigned char*ti=tile+(gg-gb)*64+i-ib;
_mm_storel_epi64((__m128i*)ti,lo);
_mm_storeh_pd((double*)(ti+64),_mm_castsi128_pd(lo));
_mm_storel_epi64((__m128i*)(ti+128),hi);
_mm_storeh_pd((double*)(ti+192),_mm_castsi128_pd(hi));
}
}
for(int gg=gb;gg<ge;gg++) memcpy(ix+(size_t)gg*IXS+ib, tile+(gg-gb)*64, 64);
}
}
}
alignas(64) __m256i tab[3][256];
const __m256i shuf=_mm256_setr_epi8(0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3);
const __m256i bits=_mm256_set1_epi64x(0x8040201008040201ULL),one=_mm256_set1_epi8(1),zero=_mm256_setzero_si256();
for(int jb=0;jb<w;jb+=4){
for(int g=0;g<groups;g+=3){
const int cnt=groups-g<3?groups-g:3;
for(int z=0;z<cnt;z++){
__m256i*Tz=tab[z];
build_gray8(pb+(size_t)((g+z)*8)*w+jb,w,Tz);
}
const unsigned char*x=ix+(size_t)g*IXS;
if(g==0){
if(cnt==3)apply_m<3,true>(n,pc,tab,x,IXS);
else if(cnt==2)apply_m<2,true>(n,pc,tab,x,IXS);
else if(cnt==1)apply_m<1,true>(n,pc,tab,x,IXS);
} else {
if(cnt==3)apply_m<3,false>(n,pc,tab,x,IXS);
else if(cnt==2)apply_m<2,false>(n,pc,tab,x,IXS);
else if(cnt==1)apply_m<1,false>(n,pc,tab,x,IXS);
}
}
for(int i=0;i<n;i++){
const uint32_t*row=(const uint32_t*)(pc+i);
for(int t=0;t<8;t++){
__m256i x=_mm256_shuffle_epi8(_mm256_set1_epi32(row[t]),shuf);
x=_mm256_min_epu8(_mm256_and_si256(x,bits),one);
_mm256_stream_si256((__m256i*)(C+(size_t)i*n+jb*64+t*32),x);
}
}
}
_mm_sfence();
free(pc);free(ix);free(pb);
}
namespace bit_winograd {
alignas(64) static __m256i tables[3][256];
alignas(64) static __m256i accum[4096];
alignas(64) static unsigned char indices[4096*512+512*128];
alignas(64) static unsigned char tile[64*64];
static __attribute__((noinline)) void leaf(int n,const uint64_t*a,int sa,const uint64_t*b,int sb,uint64_t*c,int sc){
const int groups=n/8,words=n/64;const size_t is=(size_t)n+128;
for(int ib=0;ib<n;ib+=64)for(int gb=0;gb<groups;gb+=64){
const int ge=gb+64<groups?gb+64:groups;
for(int i=ib;i<ib+64;i+=8){
for(int gg=gb;gg<ge;gg+=4){
__m128i v0,v1,v2,v3,v4,v5,v6,v7;
v0=_mm_cvtsi32_si128(*(const uint32_t*)((const unsigned char*)(a+(size_t)(i+0)*sa)+gg));
v1=_mm_cvtsi32_si128(*(const uint32_t*)((const unsigned char*)(a+(size_t)(i+1)*sa)+gg));
v2=_mm_cvtsi32_si128(*(const uint32_t*)((const unsigned char*)(a+(size_t)(i+2)*sa)+gg));
v3=_mm_cvtsi32_si128(*(const uint32_t*)((const unsigned char*)(a+(size_t)(i+3)*sa)+gg));
v4=_mm_cvtsi32_si128(*(const uint32_t*)((const unsigned char*)(a+(size_t)(i+4)*sa)+gg));
v5=_mm_cvtsi32_si128(*(const uint32_t*)((const unsigned char*)(a+(size_t)(i+5)*sa)+gg));
v6=_mm_cvtsi32_si128(*(const uint32_t*)((const unsigned char*)(a+(size_t)(i+6)*sa)+gg));
v7=_mm_cvtsi32_si128(*(const uint32_t*)((const unsigned char*)(a+(size_t)(i+7)*sa)+gg));
__m128i z0=_mm_unpacklo_epi16(_mm_unpacklo_epi8(v0,v1),_mm_unpacklo_epi8(v2,v3));
__m128i z1=_mm_unpacklo_epi16(_mm_unpacklo_epi8(v4,v5),_mm_unpacklo_epi8(v6,v7));
__m128i lo=_mm_unpacklo_epi32(z0,z1),hi=_mm_unpackhi_epi32(z0,z1);
unsigned char*ti=tile+(gg-gb)*64+i-ib;
_mm_storel_epi64((__m128i*)ti,lo);
_mm_storeh_pd((double*)(ti+64),_mm_castsi128_pd(lo));
_mm_storel_epi64((__m128i*)(ti+128),hi);
_mm_storeh_pd((double*)(ti+192),_mm_castsi128_pd(hi));
}
}
for(int gg=gb;gg<ge;gg++)memcpy(indices+(size_t)gg*is+ib,tile+(gg-gb)*64,64);
}
for(int jb=0;jb<words;jb+=4){
for(int g=0;g<groups;g+=3){
int cnt=groups-g<3?groups-g:3;
for(int z=0;z<cnt;z++)build_gray8(b+(size_t)(g+z)*8*sb+jb,sb,tables[z]);
const unsigned char*x=indices+(size_t)g*is;
if(g==0){
if(cnt==3)apply_m<3,true>(n,accum,tables,x,is);
else if(cnt==2)apply_m<2,true>(n,accum,tables,x,is);
else apply_m<1,true>(n,accum,tables,x,is);
}else{
if(cnt==3)apply_m<3,false>(n,accum,tables,x,is);
else if(cnt==2)apply_m<2,false>(n,accum,tables,x,is);
else apply_m<1,false>(n,accum,tables,x,is);
}
}
for(int i=0;i<n;i++)_mm256_store_si256((__m256i*)(c+(size_t)i*sc+jb),accum[i]);
}
}
static void form(int n,uint64_t*d,const uint64_t*a,int sa,const uint64_t*b,int sb){
const int w=n/64;
for(int i=0;i<n;i++)for(int j=0;j<w;j+=4)
_mm256_store_si256((__m256i*)(d+(size_t)i*w+j),_mm256_xor_si256(_mm256_load_si256((const __m256i*)(a+(size_t)i*sa+j)),_mm256_load_si256((const __m256i*)(b+(size_t)i*sb+j))));
}
static void reconstruct(int h,uint64_t*c,int sc,const uint64_t*m5,const uint64_t*m6,const uint64_t*m7){
const int w=h/64;
for(int i=0;i<h;i++)for(int j=0;j<w;j+=4){
uint64_t*c1=c+(size_t)i*sc+j,*c2=c1+(size_t)h*sc,*c3=c1+w,*c4=c2+w;
size_t z=(size_t)i*w+j;
__m256i p1=_mm256_load_si256((const __m256i*)c1),p2=_mm256_load_si256((const __m256i*)c2),p3=_mm256_load_si256((const __m256i*)c3),p4=_mm256_load_si256((const __m256i*)c4);
__m256i p5=_mm256_load_si256((const __m256i*)(m5+z)),p6=_mm256_load_si256((const __m256i*)(m6+z)),p7=_mm256_load_si256((const __m256i*)(m7+z));
__m256i u2=_mm256_xor_si256(p1,p6),u3=_mm256_xor_si256(u2,p7);
_mm256_store_si256((__m256i*)c1,_mm256_xor_si256(p1,p2));
_mm256_store_si256((__m256i*)c3,_mm256_xor_si256(_mm256_xor_si256(u2,p5),p3));
_mm256_store_si256((__m256i*)c2,_mm256_xor_si256(u3,p4));
_mm256_store_si256((__m256i*)c4,_mm256_xor_si256(u3,p5));
}
}
static void recurse(int n,const uint64_t*a,int sa,const uint64_t*b,int sb,uint64_t*c,int sc,uint64_t*work){
if(n<=4096){leaf(n,a,sa,b,sb,c,sc);return;}
int h=n/2,w=h/64;size_t count=(size_t)h*w;
uint64_t*s=work,*t=s+count,*m5=t+count,*m6=m5+count,*m7=m6+count,*next=m7+count;
const uint64_t*a11=a,*a12=a+w,*a21=a+(size_t)h*sa,*a22=a21+w;
const uint64_t*b11=b,*b12=b+w,*b21=b+(size_t)h*sb,*b22=b21+w;
uint64_t*c1=c,*c2=c+(size_t)h*sc,*c3=c+w,*c4=c2+w;
recurse(h,a12,sa,b21,sb,c2,sc,next);
recurse(h,a11,sa,b11,sb,c1,sc,next);
form(h,t,b12,sb,b11,sb);form(h,s,a21,sa,a22,sa);
recurse(h,s,w,t,w,m5,w,next);
form(h,s,s,w,a11,sa);form(h,t,b22,sb,t,w);
recurse(h,s,w,t,w,m6,w,next);
form(h,s,a12,sa,s,w);form(h,t,t,w,b21,sb);
recurse(h,a22,sa,t,w,c4,sc,next);
recurse(h,s,w,b22,sb,c3,sc,next);
form(h,t,b22,sb,b12,sb);form(h,s,a11,sa,a21,sa);
recurse(h,s,w,t,w,m7,w,next);
reconstruct(h,c,sc,m5,m6,m7);
}
static void pack(int n,const bool*a,uint64_t*d){
for(size_t z=0,sz=(size_t)n*n;z<sz;z+=64){
__m256i x=_mm256_load_si256((const __m256i*)(a+z));
__m256i y=_mm256_load_si256((const __m256i*)(a+z+32));
uint32_t lo=_mm256_movemask_epi8(_mm256_slli_epi16(x,7)),hi=_mm256_movemask_epi8(_mm256_slli_epi16(y,7));
d[z/64]=(uint64_t)lo|((uint64_t)hi<<32);
}
}
}
void matrix_multiply(int n,const bool*A,const bool*B,bool*C){
if(n<4096 || (n&(n-1))){fallback_product(n,A,B,C);return;}
size_t count=(size_t)n*n/64;uint64_t*mem;
if(posix_memalign((void**)&mem,4096,count*5*sizeof(uint64_t)))abort();
uint64_t*a=mem,*b=a+count,*c=b+count,*work=c+count;
bit_winograd::pack(n,A,a);bit_winograd::pack(n,B,b);
bit_winograd::recurse(n,a,n/64,b,n/64,c,n/64,work);
const __m256i shuf=_mm256_setr_epi8(0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3);
const __m256i bits=_mm256_set1_epi64x(0x8040201008040201ULL),one=_mm256_set1_epi8(1);
for(size_t z=0;z<count*2;z++){
__m256i v=_mm256_shuffle_epi8(_mm256_set1_epi32(((const uint32_t*)c)[z]),shuf);
v=_mm256_min_epu8(_mm256_and_si256(v,bits),one);
_mm256_stream_si256((__m256i*)(C+z*32),v);
}
_mm_sfence();free(mem);
}
| Compilation | N/A | N/A | Compile OK | Score: N/A | 显示更多 |
| Testcase #1 | 122.168 ms | 100 MB + 240 KB | Accepted | Score: 100 | 显示更多 |