nx_conv_speedup.nx source
↩ module page · 97 lines · 3181 B
1// THE PAYOFF: a real 2D convolution (1024x1024, 15x15 kernel), run SERIAL then THREADED (nx_thread_pool, 8
2// workers on disjoint output-row bands -- no shared writes, no atomics), with a measured wall-time speedup.
3// Pointers travel via a ctx struct (NOT module statics -- the compiler has a PRE-EXISTING multi-static bug,
4// proven pre-dating all recent compiler work), which is the same pattern nx_parallel uses.
5import "nx_thread_pool.nx"
6import "nx_fmt.nx"
7
8const IMG_W: i64 = 1024
9const IMG_H: i64 = 1024
10const K: i64 = 15
11const NWORK: i64 = 8
12
13struct ConvCtx {
14 in_ptr: i64,
15 out_ptr: i64,
16 ker_ptr: i64,
17 y0: i64,
18 y1: i64,
19}
20const CONVCTX_BYTES: i64 = 40
21
22func conv_range(in_p: *i64, out_p: *i64, ker_p: *i64, y0: i64, y1: i64) -> i64 {
23 let half: i64 = K / 2
24 var y: i64 = y0
25 while y < y1 {
26 var x: i64 = 0
27 while x < IMG_W {
28 var acc: i64 = 0
29 var ky: i64 = 0
30 while ky < K {
31 var kx: i64 = 0
32 while kx < K {
33 let iy: i64 = y + ky - half
34 let ix: i64 = x + kx - half
35 if iy >= 0 { if iy < IMG_H { if ix >= 0 { if ix < IMG_W {
36 acc = acc + in_p[iy * IMG_W + ix] * ker_p[ky * K + kx]
37 } } } }
38 kx = kx + 1
39 }
40 ky = ky + 1
41 }
42 out_p[y * IMG_W + x] = acc
43 x = x + 1
44 }
45 y = y + 1
46 }
47 return 0
48}
49
50func conv_task(ctx_i: i64) -> i64 {
51 let c: *ConvCtx = ctx_i as *ConvCtx
52 conv_range(c.in_ptr as *i64, c.out_ptr as *i64, c.ker_ptr as *i64, c.y0, c.y1)
53 return 0
54}
55
56func main() -> i64 {
57 let g_in: *i64 = sys_mmap(IMG_W * IMG_H * 8) as *i64
58 let g_out: *i64 = sys_mmap(IMG_W * IMG_H * 8) as *i64
59 let g_ker: *i64 = sys_mmap(K * K * 8) as *i64
60 var i: i64 = 0
61 while i < IMG_W * IMG_H { g_in[i] = i & 255; i = i + 1 }
62 i = 0
63 while i < K * K { g_ker[i] = (i % 7) - 3; i = i + 1 }
64
65 // SERIAL
66 let t0: i64 = sys_now_us()
67 conv_range(g_in, g_out, g_ker, 0, IMG_H)
68 let serial_us: i64 = sys_now_us() - t0
69
70 // THREADED: NWORK workers, disjoint row bands
71 let pool: *NxThreadPool = nx_pool_new(NWORK, 64)
72 let ctxs: *u8 = sys_mmap(NWORK * CONVCTX_BYTES)
73 let rp: i64 = (IMG_H + NWORK - 1) / NWORK
74 let t2: i64 = sys_now_us()
75 var w: i64 = 0
76 while w < NWORK {
77 let c: *ConvCtx = ((ctxs as i64) + w * CONVCTX_BYTES) as *ConvCtx
78 c.in_ptr = g_in as i64
79 c.out_ptr = g_out as i64
80 c.ker_ptr = g_ker as i64
81 c.y0 = w * rp
82 var yy: i64 = (w + 1) * rp
83 if yy > IMG_H { yy = IMG_H }
84 c.y1 = yy
85 nx_pool_submit(pool, conv_task, c as i64)
86 w = w + 1
87 }
88 nx_pool_wait(pool, NWORK)
89 let par_us: i64 = sys_now_us() - t2
90 nx_pool_shutdown(pool)
91
92 fmt_puts("serial_us="); fmt_putn(serial_us); fmt_puts("\n" as *u8)
93 fmt_puts("threaded_us="); fmt_putn(par_us); fmt_puts("\n" as *u8)
94 if par_us > 0 { fmt_puts("speedup_x100="); fmt_putn(serial_us * 100 / par_us); fmt_puts("\n" as *u8) }
95 sys_exit(0)
96 return 0
97}