v / bench / bench_soa_structs.v
380 lines · 351 sloc · 9.78 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
Raw
1// bench_soa_structs.v - V2 benchmark for @[soa] structs
2// Build and run with:
3// ./cmd/v2/v2 -prod -backend cleanc bench/bench_soa_structs.v -o bench/bench_soa_structs
4// ./bench/bench_soa_structs
5module main
6
7import time
8
9const particle_count = 500_000
10const build_rounds = 5
11const sum_rounds = 48
12const hot_fields_rounds = 40
13const full_struct_rounds = 20
14const integrate_rounds = 20
15
16@[soa]
17struct Particle {
18mut:
19 x f32
20 y f32
21 z f32
22 vx f32
23 vy f32
24 vz f32
25 mass f32
26 temperature f32
27 ax f32
28 ay f32
29 az f32
30 life f32
31 drag f32
32 color_r f32
33 color_g f32
34 color_b f32
35}
36
37@[typedef]
38struct C.Particle_SOA {
39 len int
40 cap int
41 x &f32
42 y &f32
43 z &f32
44 vx &f32
45 vy &f32
46 vz &f32
47 mass &f32
48 temperature &f32
49 ax &f32
50 ay &f32
51 az &f32
52 life &f32
53 drag &f32
54 color_r &f32
55 color_g &f32
56 color_b &f32
57}
58
59fn C.Particle_SOA_new(int, int) C.Particle_SOA
60fn C.Particle_SOA_push(&C.Particle_SOA, Particle)
61fn C.Particle_SOA_get(C.Particle_SOA, int) Particle
62fn C.Particle_SOA_free(&C.Particle_SOA)
63
64struct BenchResult {
65 elapsed_ms i64
66 checksum f64
67}
68
69fn make_particle(i int) Particle {
70 base := f32(i % 1024)
71 return Particle{
72 x: base * 0.25
73 y: base * 0.5
74 z: base * 0.75
75 vx: f32(0.001) * f32((i % 13) + 1)
76 vy: f32(0.0015) * f32((i % 17) + 1)
77 vz: f32(0.0005) * f32((i % 19) + 1)
78 mass: f32(1.0) + f32(i % 5) * f32(0.1)
79 temperature: f32(20.0) + f32(i % 7)
80 ax: f32(0.0001) * f32((i % 11) + 1)
81 ay: f32(0.0002) * f32((i % 9) + 1)
82 az: f32(0.0003) * f32((i % 7) + 1)
83 life: f32(100.0) - f32(i % 80)
84 drag: f32(0.98)
85 color_r: f32(i % 255) / f32(255.0)
86 color_g: f32((i * 3) % 255) / f32(255.0)
87 color_b: f32((i * 7) % 255) / f32(255.0)
88 }
89}
90
91fn build_aos() []Particle {
92 mut particles := []Particle{cap: particle_count}
93 for i in 0 .. particle_count {
94 particles << make_particle(i)
95 }
96 return particles
97}
98
99fn build_soa() C.Particle_SOA {
100 mut soa := C.Particle_SOA_new(0, particle_count)
101 for i in 0 .. particle_count {
102 C.Particle_SOA_push(&soa, make_particle(i))
103 }
104 return soa
105}
106
107fn build_soa_indexed() C.Particle_SOA {
108 mut soa := C.Particle_SOA_new(particle_count, particle_count)
109 unsafe {
110 for i in 0 .. particle_count {
111 p := make_particle(i)
112 soa.x[i] = p.x
113 soa.y[i] = p.y
114 soa.z[i] = p.z
115 soa.vx[i] = p.vx
116 soa.vy[i] = p.vy
117 soa.vz[i] = p.vz
118 soa.mass[i] = p.mass
119 soa.temperature[i] = p.temperature
120 soa.ax[i] = p.ax
121 soa.ay[i] = p.ay
122 soa.az[i] = p.az
123 soa.life[i] = p.life
124 soa.drag[i] = p.drag
125 soa.color_r[i] = p.color_r
126 soa.color_g[i] = p.color_g
127 soa.color_b[i] = p.color_b
128 }
129 }
130 return soa
131}
132
133fn bench_build_aos() BenchResult {
134 sw := time.new_stopwatch()
135 mut sum := f64(0.0)
136 for _ in 0 .. build_rounds {
137 particles := build_aos()
138 sum += particles.len
139 sum += particles[0].x
140 sum += particles[particles.len - 1].life
141 }
142 return BenchResult{
143 elapsed_ms: sw.elapsed().milliseconds()
144 checksum: sum
145 }
146}
147
148fn bench_build_soa() BenchResult {
149 sw := time.new_stopwatch()
150 mut sum := f64(0.0)
151 for _ in 0 .. build_rounds {
152 mut soa := build_soa()
153 sum += soa.len
154 unsafe {
155 sum += soa.x[0]
156 sum += soa.life[soa.len - 1]
157 }
158 C.Particle_SOA_free(&soa)
159 }
160 return BenchResult{
161 elapsed_ms: sw.elapsed().milliseconds()
162 checksum: sum
163 }
164}
165
166fn bench_build_soa_indexed() BenchResult {
167 sw := time.new_stopwatch()
168 mut sum := f64(0.0)
169 for _ in 0 .. build_rounds {
170 mut soa := build_soa_indexed()
171 sum += soa.len
172 unsafe {
173 sum += soa.x[0]
174 sum += soa.life[soa.len - 1]
175 }
176 C.Particle_SOA_free(&soa)
177 }
178 return BenchResult{
179 elapsed_ms: sw.elapsed().milliseconds()
180 checksum: sum
181 }
182}
183
184fn bench_sum_x_aos(particles []Particle) BenchResult {
185 sw := time.new_stopwatch()
186 mut sum := f64(0.0)
187 for _ in 0 .. sum_rounds {
188 for i in 0 .. particles.len {
189 sum += particles[i].x
190 }
191 }
192 return BenchResult{
193 elapsed_ms: sw.elapsed().milliseconds()
194 checksum: sum
195 }
196}
197
198fn bench_sum_x_soa(soa C.Particle_SOA) BenchResult {
199 sw := time.new_stopwatch()
200 mut sum := f64(0.0)
201 for _ in 0 .. sum_rounds {
202 unsafe {
203 for i in 0 .. soa.len {
204 sum += soa.x[i]
205 }
206 }
207 }
208 return BenchResult{
209 elapsed_ms: sw.elapsed().milliseconds()
210 checksum: sum
211 }
212}
213
214fn bench_sum_hot_fields_aos(particles []Particle) BenchResult {
215 sw := time.new_stopwatch()
216 mut sum := f64(0.0)
217 for _ in 0 .. hot_fields_rounds {
218 for i in 0 .. particles.len {
219 sum += particles[i].x + particles[i].y + particles[i].z + particles[i].life
220 }
221 }
222 return BenchResult{
223 elapsed_ms: sw.elapsed().milliseconds()
224 checksum: sum
225 }
226}
227
228fn bench_sum_hot_fields_soa(soa C.Particle_SOA) BenchResult {
229 sw := time.new_stopwatch()
230 mut sum := f64(0.0)
231 for _ in 0 .. hot_fields_rounds {
232 unsafe {
233 for i in 0 .. soa.len {
234 sum += soa.x[i] + soa.y[i] + soa.z[i] + soa.life[i]
235 }
236 }
237 }
238 return BenchResult{
239 elapsed_ms: sw.elapsed().milliseconds()
240 checksum: sum
241 }
242}
243
244fn bench_sum_all_fields_aos(particles []Particle) BenchResult {
245 sw := time.new_stopwatch()
246 mut sum := f64(0.0)
247 for _ in 0 .. full_struct_rounds {
248 for i in 0 .. particles.len {
249 sum += particles[i].x + particles[i].y + particles[i].z + particles[i].vx +
250 particles[i].vy + particles[i].vz + particles[i].mass + particles[i].temperature +
251 particles[i].ax + particles[i].ay + particles[i].az + particles[i].life +
252 particles[i].drag + particles[i].color_r + particles[i].color_g +
253 particles[i].color_b
254 }
255 }
256 return BenchResult{
257 elapsed_ms: sw.elapsed().milliseconds()
258 checksum: sum
259 }
260}
261
262fn bench_sum_all_fields_soa(soa C.Particle_SOA) BenchResult {
263 sw := time.new_stopwatch()
264 mut sum := f64(0.0)
265 for _ in 0 .. full_struct_rounds {
266 for i in 0 .. soa.len {
267 p := C.Particle_SOA_get(soa, i)
268 sum += p.x + p.y + p.z + p.vx + p.vy + p.vz + p.mass + p.temperature + p.ax + p.ay +
269 p.az + p.life + p.drag + p.color_r + p.color_g + p.color_b
270 }
271 }
272 return BenchResult{
273 elapsed_ms: sw.elapsed().milliseconds()
274 checksum: sum
275 }
276}
277
278fn bench_integrate_aos(mut particles []Particle) BenchResult {
279 sw := time.new_stopwatch()
280 for _ in 0 .. integrate_rounds {
281 for i in 0 .. particles.len {
282 particles[i].vx += particles[i].ax
283 particles[i].vy += particles[i].ay
284 particles[i].vz += particles[i].az
285 particles[i].x += particles[i].vx * particles[i].mass
286 particles[i].y += particles[i].vy * particles[i].mass
287 particles[i].z += particles[i].vz * particles[i].mass
288 particles[i].life -= particles[i].drag
289 }
290 }
291 mut sum := f64(0.0)
292 for i in 0 .. particles.len {
293 sum += particles[i].x + particles[i].y + particles[i].z + particles[i].life
294 }
295 return BenchResult{
296 elapsed_ms: sw.elapsed().milliseconds()
297 checksum: sum
298 }
299}
300
301fn bench_integrate_soa(mut soa C.Particle_SOA) BenchResult {
302 sw := time.new_stopwatch()
303 unsafe {
304 for _ in 0 .. integrate_rounds {
305 for i in 0 .. soa.len {
306 soa.vx[i] += soa.ax[i]
307 soa.vy[i] += soa.ay[i]
308 soa.vz[i] += soa.az[i]
309 soa.x[i] += soa.vx[i] * soa.mass[i]
310 soa.y[i] += soa.vy[i] * soa.mass[i]
311 soa.z[i] += soa.vz[i] * soa.mass[i]
312 soa.life[i] -= soa.drag[i]
313 }
314 }
315 }
316 mut sum := f64(0.0)
317 unsafe {
318 for i in 0 .. soa.len {
319 sum += soa.x[i] + soa.y[i] + soa.z[i] + soa.life[i]
320 }
321 }
322 return BenchResult{
323 elapsed_ms: sw.elapsed().milliseconds()
324 checksum: sum
325 }
326}
327
328fn print_result(name string, aos BenchResult, soa BenchResult) {
329 println(name)
330 println(' aos: ${aos.elapsed_ms} ms, checksum=${aos.checksum}')
331 println(' soa: ${soa.elapsed_ms} ms, checksum=${soa.checksum}')
332 if soa.elapsed_ms > 0 {
333 println(' speedup: ${f64(aos.elapsed_ms) / f64(soa.elapsed_ms)}x')
334 }
335}
336
337fn print_build_results(aos BenchResult, soa_push BenchResult, soa_indexed BenchResult) {
338 println('build particles')
339 println(' aos: ${aos.elapsed_ms} ms, checksum=${aos.checksum}')
340 println(' soa push: ${soa_push.elapsed_ms} ms, checksum=${soa_push.checksum}')
341 println(' soa indexed: ${soa_indexed.elapsed_ms} ms, checksum=${soa_indexed.checksum}')
342 if soa_indexed.elapsed_ms > 0 {
343 println(' push vs indexed: ${f64(soa_push.elapsed_ms) / f64(soa_indexed.elapsed_ms)}x')
344 println(' indexed speedup over aos: ${f64(aos.elapsed_ms) / f64(soa_indexed.elapsed_ms)}x')
345 }
346}
347
348fn main() {
349 println('soa struct bench (v2 cleanc)')
350 println('particle_count=${particle_count}, build_rounds=${build_rounds}, sum_rounds=${sum_rounds}, hot_fields_rounds=${hot_fields_rounds}, full_struct_rounds=${full_struct_rounds}, integrate_rounds=${integrate_rounds}')
351
352 result_build_aos := bench_build_aos()
353 result_build_soa_push := bench_build_soa()
354 result_build_soa_indexed := bench_build_soa_indexed()
355 print_build_results(result_build_aos, result_build_soa_push, result_build_soa_indexed)
356
357 aos_scan := build_aos()
358 soa_scan := build_soa()
359 result_aos_scan := bench_sum_x_aos(aos_scan)
360 result_soa_scan := bench_sum_x_soa(soa_scan)
361 print_result('sum x only', result_aos_scan, result_soa_scan)
362
363 result_aos_hot_fields := bench_sum_hot_fields_aos(aos_scan)
364 result_soa_hot_fields := bench_sum_hot_fields_soa(soa_scan)
365 print_result('sum x/y/z/life', result_aos_hot_fields, result_soa_hot_fields)
366
367 result_aos_full_struct := bench_sum_all_fields_aos(aos_scan)
368 result_soa_full_struct := bench_sum_all_fields_soa(soa_scan)
369 print_result('materialize full struct and sum all fields', result_aos_full_struct,
370 result_soa_full_struct)
371 C.Particle_SOA_free(&soa_scan)
372
373 mut aos_integrate := build_aos()
374 mut soa_integrate := build_soa()
375 result_aos_integrate := bench_integrate_aos(mut aos_integrate)
376 result_soa_integrate := bench_integrate_soa(mut soa_integrate)
377 print_result('integrate position, velocity, and life', result_aos_integrate,
378 result_soa_integrate)
379 C.Particle_SOA_free(&soa_integrate)
380}
381