From 6aab84bf4b47042c6976071b1d6476e451673c5b Mon Sep 17 00:00:00 2001 From: FallenSigh Date: Fri, 26 Jun 2026 17:44:13 +0800 Subject: [PATCH] perf(matmul): remove diagnostic get_ns() calls from hot loop (v2 clean) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stripped all per-iteration timing instrumentation: - Removed 16 get_ns() calls per iteration from main loop - Removed pipeline diagnostic print section - Removed timing accumulator variables - Retained outer timing markers only (prologue/loop/epilogue) Expected ~45% speedup from removing measurement overhead (previous run: 179.6ms with diag → estimated ~100ms clean) --- sdk/software/examples/asm/user-sample.c | 95 +++++-------------------- 1 file changed, 19 insertions(+), 76 deletions(-) diff --git a/sdk/software/examples/asm/user-sample.c b/sdk/software/examples/asm/user-sample.c index 2a09c60..124aa31 100644 --- a/sdk/software/examples/asm/user-sample.c +++ b/sdk/software/examples/asm/user-sample.c @@ -8,9 +8,6 @@ * 2. Inlined wait loops → zero function-call overhead in hot path * 3. Inlined DMA start → skips redundant STATUS_BUSY_BIT check * 4. XOR bank toggle → avoids g%2 recomputation per iteration - * - * v2.1: Pipeline timing diagnostics — measures per-phase wait times - * and stall counts to identify the real bottleneck (IP vs DMA vs store). */ #include @@ -21,7 +18,7 @@ #include #include -/* ─── Configurable group count (default 5000 for diagnostics) ──────── */ +/* ─── Configurable group count (default 5000) ──────────────────────── */ #ifndef MATMUL_GROUP_NUM #define MATMUL_GROUP_NUM 5000 #endif @@ -86,18 +83,6 @@ int main(void) uint32_t phys_bank_C[2] = {MATMUL_PHYS_B0_C, MATMUL_PHYS_B1_C}; uint32_t ctrl_start[2] = {CTRL_START_B0, CTRL_START_B1}; - /* ── Pipeline timing diagnostics ──────────────────────────────── */ - uint32_t t_now; - uint32_t t_wait_ip = 0; // total ns waiting for IP done - uint32_t t_wait_dma0 = 0; // total ns waiting for DMA Ch0 done - uint32_t t_wait_dma1 = 0; // total ns waiting for DMA Ch1 done - uint32_t t_fire_dma = 0; // total ns writing DMA registers - uint32_t t_fire_ip = 0; // total ns writing IP ctrl register - int n_loops = 0; // main loop iteration count - int stall_ip = 0; // iterations where wait_ip actually blocked - int stall_dma0 = 0; // iterations where wait_dma0 actually blocked - int stall_dma1 = 0; // iterations where wait_dma1 actually blocked - /* ── Start marker (must be first output) ──────────────────── */ printf("MATMUL_START\n"); @@ -135,76 +120,60 @@ int main(void) /* ═══════════════════════════════════════════════════════════════ * MAIN LOOP: Fully-pipelined — pre-fire DMA load at loop tail - * + per-phase timing diagnostics + * + * At iteration g: + * IP is computing group (g-1) on prev_bank + * Ch0 loaded group g to cur_bank (pre-fired in iteration g-1) + * + * Pipeline: + * Ch0: | load g+1 | | load g+2 | + * Ch1: | store g-1 | | store g | + * IP : | compute g | | compute g+1 | * ═══════════════════════════════════════════════════════════════ */ t_mainloop = get_ns(); - bank = 1; - - // Prologue IP start time (for measuring first IP compute duration) - // We can't easily isolate pure IP time, but aggregate wait tells us. + bank = 1; // cur_bank for iteration g=1; prev_bank = bank ^ 1 for (g = 1; g < MATMUL_GROUP_NUM; g++) { int cur_bank = bank; int prev_bank = bank ^ 1; - /* ── 1. Wait for previous IP computation ─────────────────── */ - t_now = get_ns(); + // 1. Wait for previous IP computation (group g-1 on prev_bank) while (!(*mat_status & STATUS_DONE)) {} *mat_status = STATUS_DONE; - { - uint32_t dt = get_ns() - t_now; - t_wait_ip += dt; - if (dt > 0) stall_ip++; - } - /* ── 2. Wait for previous DMA Ch1 store ──────────────────── */ + // 2. Ensure previous DMA Ch1 store is done (group g-2) if (g > 1) { - t_now = get_ns(); while (!(ch1->STATUS & STATUS_DONE_BIT)) {} ch1->STATUS = STATUS_DONE_BIT; - uint32_t dt = get_ns() - t_now; - t_wait_dma1 += dt; - if (dt > 0) stall_dma1++; } - /* ── 3. Start DMA Ch1 store: group (g-1) result → ExtRAM ── */ - t_now = get_ns(); + // 3. Start DMA Ch1 store: group (g-1) result → ExtRAM ch1->SRC_ADDR = phys_bank_C[prev_bank]; ch1->DST_ADDR = EXTRAM_PHYS_C(g - 1); ch1->LENGTH = 192; ch1->TAG = 20; ch1->CTRL = DMA_CTRL_VAL; - t_fire_dma += (get_ns() - t_now); - /* ── 4. Wait for DMA Ch0 load (pre-fired in prev iteration) */ - t_now = get_ns(); + // 4. Wait for current DMA Ch0 load (group g → cur_bank, pre-fired in iteration g-1) while (!(ch0->STATUS & STATUS_DONE_BIT)) {} ch0->STATUS = STATUS_DONE_BIT; - { - uint32_t dt = get_ns() - t_now; - t_wait_dma0 += dt; - if (dt > 0) stall_dma0++; - } - /* ── 5. Start IP computation ─────────────────────────────── */ - t_now = get_ns(); + // 5. Start IP computation for current group on cur_bank *mat_ctrl = ctrl_start[cur_bank]; - t_fire_ip += (get_ns() - t_now); - /* ── 6. PRE-FIRE DMA Ch0 load for next group ─────────────── */ + // ★ 6. PRE-FIRE: DMA Ch0 load next group (g+1) → next_bank + // This runs CONCURRENTLY with IP computation started in step 5! if (g < MATMUL_GROUP_NUM - 1) { int next_bank = bank ^ 1; - t_now = get_ns(); ch0->SRC_ADDR = EXTRAM_PHYS_AB(g + 1); ch0->DST_ADDR = phys_bank_AB[next_bank]; ch0->LENGTH = 128; ch0->TAG = 10; ch0->CTRL = DMA_CTRL_VAL; - t_fire_dma += (get_ns() - t_now); } + // Toggle bank for next iteration bank ^= 1; - n_loops++; } t_mainloop = get_ns() - t_mainloop; @@ -241,32 +210,6 @@ int main(void) printf("TIME_LOOP_PER=%lu\n", (unsigned long)(t_mainloop / MATMUL_GROUP_NUM)); printf("TIME_EPILOGUE=%lu\n", (unsigned long)t_epilogue); printf("TIME_TOTAL=%lu\n", (unsigned long)(t_prologue + t_mainloop + t_epilogue)); - - /* ═══════════════════════════════════════════════════════════════ - * Pipeline Timing Diagnostics - * - * Interpretation guide: - * PIPE_WAIT_DMA0 ≈ 0 → pre-fire working, DMA load never blocks - * PIPE_WAIT_DMA0 > 0 → DMA load is bottleneck (wider bus / faster RAM) - * PIPE_WAIT_IP > 0 → IP compute is bottleneck (optimize multiplier HW) - * PIPE_WAIT_DMA1 > 0 → DMA store is bottleneck (bank reuse delay) - * PIPE_STALL_* = N → that resource blocked EVERY iteration - * - * All times in nanoseconds. Averages over N-1 loop iterations. - * ═══════════════════════════════════════════════════════════════ */ - printf("--- Pipeline Profile (%d loop iterations) ---\n", n_loops); - printf("PIPE_WAIT_IP=%lu\n", (unsigned long)t_wait_ip); - printf("PIPE_WAIT_IP_AVG=%lu\n", (unsigned long)(n_loops ? t_wait_ip / n_loops : 0)); - printf("PIPE_WAIT_DMA0=%lu\n", (unsigned long)t_wait_dma0); - printf("PIPE_WAIT_DMA0_AVG=%lu\n", (unsigned long)(n_loops ? t_wait_dma0 / n_loops : 0)); - printf("PIPE_WAIT_DMA1=%lu\n", (unsigned long)t_wait_dma1); - printf("PIPE_WAIT_DMA1_AVG=%lu\n", (unsigned long)(n_loops ? t_wait_dma1 / n_loops : 0)); - printf("PIPE_FIRE_DMA=%lu\n", (unsigned long)t_fire_dma); - printf("PIPE_FIRE_IP=%lu\n", (unsigned long)t_fire_ip); - printf("PIPE_STALL_IP=%d\n", stall_ip); - printf("PIPE_STALL_DMA0=%d\n", stall_dma0); - printf("PIPE_STALL_DMA1=%d\n", stall_dma1); - printf("PIPE_SUM_WAITS=%lu\n", (unsigned long)(t_wait_ip + t_wait_dma0 + t_wait_dma1 + t_fire_dma + t_fire_ip)); printf("MATMUL_DONE\n"); return 0;