-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
86 lines (73 loc) · 3.67 KB
/
Copy pathmain.cpp
File metadata and controls
86 lines (73 loc) · 3.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <cstdio>
#include <vector>
#include "config.hpp"
#include "isa.hpp"
#include "config_io.hpp"
#include "machine.hpp"
#include "sim.hpp"
#include "trace.hpp"
#include "selfcheck.hpp"
#include "lower.hpp"
#include "golden.hpp"
int main() {
Config cfg = load_config("config/default.toml");
Machine m;
m.cfg = cfg;
allocate(m, /*main_bytes*/ 1 << 16);
// a and b tiles are all 1s (8x8 int8 each, at [0,64) and [64,128)) so every c[i][j] = 8
for (std::size_t b = 0; b < 128; ++b) m.mem.data[b] = 1;
std::vector<Inst> prog = {
Config_{cfg.dataflow},
Load{/*src*/ 0, {0, 0}, Queue::Dma, 1, 64}, // A tile -> bank 0
Load{/*src*/ 64, {1, 0}, Queue::Dma, 1, 64}, // B tile -> bank 1
Matmul{{0, 0}, {1, 0}, {2, 0}, /*k*/ 8}, // -> acc bank 2
Store{{2, 0}, /*dst*/ 128, Queue::Dma, 1, 256},
};
print_program(prog);
Counters c = run(m, prog);
print_counters(c, cfg); // show report
std::printf("result = C[0][0]=%d C[7][7]=%d (expect 8)\n",
m.accbanks.bank[2][0], m.accbanks.bank[2][63]);
run_self_checks(cfg);
// lower a gemm bigger than the array
{
const std::size_t M = 20, N = 12, K = 24; // spills R/C and split-K over 8x8
std::vector<Elem> A(M * K), B(K * N);
for (std::size_t i = 0; i < A.size(); ++i) A[i] = static_cast<Elem>((i % 5) - 2);
for (std::size_t i = 0; i < B.size(); ++i) B[i] = static_cast<Elem>((i % 3) - 1);
Machine gm; gm.cfg = cfg; allocate(gm, 1 << 20);
gm.out.assign(M * N, Acc{0});
for (std::size_t i = 0; i < A.size(); ++i) gm.mem.data[i] = A[i];
for (std::size_t i = 0; i < B.size(); ++i) gm.mem.data[A.size() + i] = B[i];
const std::vector<Inst> prog = lower(GemmSpec{M, N, K, 0, A.size()}, cfg);
const Counters gc = run(gm, prog);
const std::vector<Acc> want = reference_gemm_mnk(M, N, K, A.data(), B.data());
const bool ok = std::vector<Acc>(gm.out.begin(), gm.out.begin() + M * N) == want;
std::printf("\n=== lowered GEMM %zux%zux%zu ===\n", M, N, K);
std::printf("instructions = %zu\n", prog.size());
std::printf("total_cycles = %u\n", gc.total_cycles);
std::printf("result = %s golden\n", ok ? "matches" : "DIFFERS from");
}
// naive vs pipelined, show the speedup
{
const std::size_t M = 8, N = 8, K = 64; // lots of k so overlap does work
std::vector<Elem> A(M * K), B(K * N);
for (std::size_t i = 0; i < A.size(); ++i) A[i] = static_cast<Elem>((i % 5) - 2);
for (std::size_t i = 0; i < B.size(); ++i) B[i] = static_cast<Elem>((i % 3) - 1);
auto run_mode = [&](Sched sched) {
Machine gm; gm.cfg = cfg; allocate(gm, 1 << 20);
gm.out.assign(M * N, Acc{0});
for (std::size_t i = 0; i < A.size(); ++i) gm.mem.data[i] = A[i];
for (std::size_t i = 0; i < B.size(); ++i) gm.mem.data[A.size() + i] = B[i];
return run(gm, lower(GemmSpec{M, N, K, 0, A.size()}, cfg, sched));
};
const Counters naive = run_mode(Sched::Naive);
const Counters pipe = run_mode(Sched::Pipelined);
std::printf("\n=== before/after GEMM %zux%zux%zu ===\n", M, N, K);
std::printf("naive total = %u util = %.1f%%\n", naive.total_cycles, naive.utilization * 100.0f);
std::printf("pipelined total = %u util = %.1f%%\n", pipe.total_cycles, pipe.utilization * 100.0f);
std::printf("speedup = %.2fx\n",
pipe.total_cycles ? static_cast<float>(naive.total_cycles) / pipe.total_cycles : 0.0f);
}
return 0;
}