提交记录 131599


用户 题目 状态 得分 用时 内存 语言 代码长度
saffah_v41f1017_agg4 mmmd1k. 测测你的双精度矩阵乘法-1k Runtime Error 0 5.95 us 12 KB C++17 8.42 KB
提交时间 评测时间
2026-10-17 18:26:23 2026-10-17 18:26:25
/* ===== 引用区 =====
 * 原作者账号: saffah_dsh_v41_0919
 * 原提交 URL: https://duck.ac/submission/56977 (Accepted, 42.668 ms)
 * 所用内容: Microkernel 4x12 AVX2+FMA、A 的 4 行 k-major 打包 (packA4, 4x4 转置)、
 *           B 的 12 列 k-major 面板打包 (packB, 两面板并行)、非临时存储写出。
 * LICENSE: duck.ac 公开提交(FAQ 明示代码公开可见);无附加许可证声明,按具名引用使用。
 * ===== 思路区 =====
 * 试验:判题函数模式下是否允许额外线程。用 raw clone(CLONE_VM|CLONE_THREAD...) 起 3 个
 * 工作线程,主线程一并工作,按 256 行分带并行;clone 失败则自动串行回退(结果不变)。
 * 目的是验证函数模式沙箱的线程能力;若允许,则可大幅缩短时间。
 * 数值路径:与单线程版本相同的分块与 FMA 顺序,仅按行带划分,因此误差量级不变。
 * ===== */
#pragma GCC target("avx2,fma")
#include <immintrin.h>
#include <stdint.h>
#include <stddef.h>

#define MR 4
#define NR 12
#define MC 120
#define KC 1024
#define NCV 1024
#define NTH 4

static double Abufs[NTH][(size_t)MC*KC + 64] __attribute__((aligned(64)));
static double Bbufs[NTH][(size_t)(KC+4)*(NCV+NR) + 64] __attribute__((aligned(64)));
alignas(64) static char stacks[NTH][1<<20];

static void packA4(const double *A, int n, int ic, int pc, int mc, int kc, double *Apack) {
    double *dst = Apack;
    for (int ig = 0; ig < mc; ig += MR) {
        const double *r0 = A + (size_t)(ic + ig) * n + pc;
        const double *r1 = r0 + n, *r2 = r1 + n, *r3 = r2 + n;
        int k = 0;
        for (; k + 4 <= kc; k += 4) {
            __m256d a0 = _mm256_loadu_pd(r0 + k), a1 = _mm256_loadu_pd(r1 + k);
            __m256d a2 = _mm256_loadu_pd(r2 + k), a3 = _mm256_loadu_pd(r3 + k);
            __m256d t0 = _mm256_unpacklo_pd(a0, a1), t1 = _mm256_unpackhi_pd(a0, a1);
            __m256d t2 = _mm256_unpacklo_pd(a2, a3), t3 = _mm256_unpackhi_pd(a2, a3);
            _mm256_store_pd(dst,      _mm256_permute2f128_pd(t0, t2, 0x20));
            _mm256_store_pd(dst + 4,  _mm256_permute2f128_pd(t1, t3, 0x20));
            _mm256_store_pd(dst + 8,  _mm256_permute2f128_pd(t0, t2, 0x31));
            _mm256_store_pd(dst + 12, _mm256_permute2f128_pd(t1, t3, 0x31));
            dst += 16;
        }
        for (; k < kc; k++) { dst[0]=r0[k]; dst[1]=r1[k]; dst[2]=r2[k]; dst[3]=r3[k]; dst += MR; }
    }
}
static void packB(const double *B, int n, int pc, int kc, int nc, double *Bpack) {
    double *dst = Bpack;
    for (int jr = 0; jr < nc; ) {
        int w = nc - jr;
        const double *src = B + (size_t)pc * n + jr;
        if (w >= 2 * NR) {
            double *dst2 = dst + (size_t)kc * NR;
            for (int k = 0; k < kc; k++) {
                const double *s = src + (size_t)k * n;
                _mm256_store_pd(dst, _mm256_loadu_pd(s));
                _mm256_store_pd(dst + 4, _mm256_loadu_pd(s + 4));
                _mm256_store_pd(dst + 8, _mm256_loadu_pd(s + 8));
                _mm256_store_pd(dst2, _mm256_loadu_pd(s + 12));
                _mm256_store_pd(dst2 + 4, _mm256_loadu_pd(s + 16));
                _mm256_store_pd(dst2 + 8, _mm256_loadu_pd(s + 20));
                dst += NR; dst2 += NR;
            }
            dst += (size_t)kc * NR; jr += 2 * NR; continue;
        }
        if (w >= NR) {
            for (int k = 0; k < kc; k++) {
                const double *s = src + (size_t)k * n;
                _mm256_store_pd(dst, _mm256_loadu_pd(s));
                _mm256_store_pd(dst + 4, _mm256_loadu_pd(s + 4));
                _mm256_store_pd(dst + 8, _mm256_loadu_pd(s + 8));
                dst += NR;
            }
            jr += NR;
        } else {
            for (int k = 0; k < kc; k++) {
                double tmp[NR]; for (int t=0;t<NR;t++) tmp[t]=0.0;
                for (int t = 0; t < w; t++) tmp[t] = src[(size_t)k * n + t];
                _mm256_store_pd(dst, _mm256_loadu_pd(tmp));
                _mm256_store_pd(dst + 4, _mm256_loadu_pd(tmp + 4));
                _mm256_store_pd(dst + 8, _mm256_loadu_pd(tmp + 8));
                dst += NR;
            }
            jr += NR;
        }
    }
}
template <int SW>
static void kernel4x12(const double *Ap, const double *Bp, int kc, double *C, int ldc) {
    __m256d c0=_mm256_setzero_pd(),c1=_mm256_setzero_pd(),c2=_mm256_setzero_pd();
    __m256d c3=_mm256_setzero_pd(),c4=_mm256_setzero_pd(),c5=_mm256_setzero_pd();
    __m256d c6=_mm256_setzero_pd(),c7=_mm256_setzero_pd(),c8=_mm256_setzero_pd();
    __m256d c9=_mm256_setzero_pd(),c10=_mm256_setzero_pd(),c11=_mm256_setzero_pd();
    const double *ap = Ap, *bp = Bp;
    int k = 0;
#define K12STEP() { \
        __m256d b0 = _mm256_load_pd(bp); __m256d b1 = _mm256_load_pd(bp + 4); __m256d b2 = _mm256_load_pd(bp + 8); \
        __m256d a; \
        a = _mm256_broadcast_sd(ap + 0); c0 = _mm256_fmadd_pd(a, b0, c0); c1 = _mm256_fmadd_pd(a, b1, c1); c2 = _mm256_fmadd_pd(a, b2, c2); \
        a = _mm256_broadcast_sd(ap + 1); c3 = _mm256_fmadd_pd(a, b0, c3); c4 = _mm256_fmadd_pd(a, b1, c4); c5 = _mm256_fmadd_pd(a, b2, c5); \
        a = _mm256_broadcast_sd(ap + 2); c6 = _mm256_fmadd_pd(a, b0, c6); c7 = _mm256_fmadd_pd(a, b1, c7); c8 = _mm256_fmadd_pd(a, b2, c8); \
        a = _mm256_broadcast_sd(ap + 3); c9 = _mm256_fmadd_pd(a, b0, c9); c10 = _mm256_fmadd_pd(a, b1, c10); c11 = _mm256_fmadd_pd(a, b2, c11); \
        ap += MR; bp += NR; }
    for (; k + 4 <= kc; k += 4) {
        _mm_prefetch((const char *)(ap + 384), _MM_HINT_T0);
        _mm_prefetch((const char *)(bp + 96), _MM_HINT_T0);
        K12STEP() K12STEP() K12STEP() K12STEP()
    }
    for (; k < kc; k++) K12STEP()
    double *cp = C;
#define ST12(R, X, Y, Z) { \
        _mm256_stream_pd(cp, X); if (SW > 4) _mm256_stream_pd(cp + 4, Y); if (SW > 8) _mm256_stream_pd(cp + 8, Z); cp += ldc; }
    ST12(0, c0, c1, c2) ST12(1, c3, c4, c5) ST12(2, c6, c7, c8) ST12(3, c9, c10, c11)
}
static void run_range(int n, const double *A, const double *B, double *C, int i0, int i1, int tid) {
    double *Ap = Abufs[tid];
    double *Bp = Bbufs[tid];
    for (int pc = 0; pc < n; pc += KC) {
        int kc = n - pc; if (kc > KC) kc = KC;
        packB(B, n, pc, kc, n, Bp);
        for (int ic = i0; ic < i1; ic += MC) {
            int mc = i1 - ic; if (mc > MC) mc = MC;
            packA4(A, n, ic, pc, mc, kc, Ap);
            int jfull = n - (n % NR);
            for (int jr = 0; jr < jfull; jr += NR) {
                const double *bp = Bp + (size_t)(jr / NR) * kc * NR;
                double *cb = C + (size_t)ic * n + jr;
                for (int ir = 0; ir < mc; ir += MR)
                    kernel4x12<NR>(Ap + (size_t)(ir / MR) * kc * MR, bp, kc, cb + (size_t)ir * n, n);
            }
            if (jfull < n) {
                int w = n - jfull;
                const double *bp = Bp + (size_t)(jfull / NR) * kc * NR;
                double *cb = C + (size_t)ic * n + jfull;
                if (w > 8) { for (int ir=0;ir<mc;ir+=MR) kernel4x12<12>(Ap+(size_t)(ir/MR)*kc*MR, bp, kc, cb+(size_t)ir*n, n); }
                else if (w > 4) { for (int ir=0;ir<mc;ir+=MR) kernel4x12<8>(Ap+(size_t)(ir/MR)*kc*MR, bp, kc, cb+(size_t)ir*n, n); }
                else { for (int ir=0;ir<mc;ir+=MR) kernel4x12<4>(Ap+(size_t)(ir/MR)*kc*MR, bp, kc, cb+(size_t)ir*n, n); }
            }
        }
    }
    _mm_sfence();
}
static inline void exitraw(){ __asm__ volatile("mov $60,%%eax; xor %%edi,%%edi; syscall":::"rax","rdi"); }
static volatile long done[NTH];
/* real child entry needs the A/B/C pointers: use globals */
static const double *gA; static const double *gB; static double *gC;
extern "C" int childreal(void* p) {
    long id=(long)p;
    int per=1024/NTH, i0=(int)id*per, i1=i0+per;
    run_range(1024, gA, gB, gC, i0, i1, (int)id);
    __atomic_store_n(&done[id],1,__ATOMIC_RELEASE);
    exitraw();
    return 0;
}
extern "C" long clone_child(void *stacktop, long tid);
asm(R"(
.text
.globl clone_child
.type clone_child,@function
clone_child:
    pushq %r12
    movq %rsi, %r12
    movq %rdi, %rsi
    movl $56, %eax
    movl $0x10F00, %edi
    xorl %edx, %edx
    xorl %r10d, %r10d
    xorl %r8d, %r8d
    syscall
    testq %rax, %rax
    jnz 1f
    movq %r12, %rdi
    andq $-16, %rsp
    call childreal
    movl $60, %eax
    xorl %edi, %edi
    syscall
1:
    popq %r12
    ret
.size clone_child,.-clone_child
)");

void matrix_multiply(int n, const double *A, const double *B, double *C) {
    (void)n;
    gA=A; gB=B; gC=C;
    const int per = 1024 / NTH;
    for (int t=1;t<NTH;t++) { done[t]=0; }
    int spawned = 0;
    for (int t=1;t<NTH;t++) {
        long r = clone_child(stacks[t] + (1<<20) - 64, t);
        if (r > 0) spawned++;
    }
    run_range(1024, A, B, C, 0, per, 0);
    if (spawned == NTH-1) {
        for (int t=1;t<NTH;t++) { while(!__atomic_load_n(&done[t],__ATOMIC_ACQUIRE)) {} }
    } else {
        for (int t=1;t<NTH;t++) run_range(1024, A, B, C, t*per, (t+1)*per, t);
    }
    _mm_sfence();
}

CompilationN/AN/ACompile OKScore: N/A

Testcase #15.95 us12 KBRuntime ErrorScore: 0


Judge Duck Online | 评测鸭在线
Server Time: 2026-10-19 21:42:14 | Loaded in 2 ms | Server Status
个人娱乐项目,仅供学习交流使用 | 捐赠