aboutsummaryrefslogtreecommitdiff
path: root/tests/benchmark/bytes/benchmark_bytes.odin
blob: ee3a91d6405f4fb56be3f803782a23ef1009d017 (plain)
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package benchmark_bytes

import "core:bytes"
import "core:fmt"
import "core:log"
import "core:testing"
import "core:strings"
import "core:text/table"
import "core:time"

RUNS_PER_SIZE :: 2500

sizes := [?]int {
	15, 16, 17,
	31, 32, 33,
	63, 64, 65,
	128,
	256,
	512,
	1024,
	4096,
	1024 * 1024,
	// 1024 * 1024 * 1024,
}

// These are the normal, unoptimized algorithms.

plain_index_byte :: proc "contextless" (s: []u8, c: byte) -> (res: int) #no_bounds_check {
	for i := 0; i < len(s); i += 1 {
		if s[i] == c {
			return i
		}
	}
	return -1
}

plain_last_index_byte :: proc "contextless" (s: []u8, c: byte) -> (res: int) #no_bounds_check {
	for i := len(s)-1; i >= 0; i -= 1 {
		if s[i] == c {
			return i
		}
	}
	return -1
}

run_trial_size :: proc(p: proc "contextless" ([]u8, byte) -> int, size: int, idx: int, runs: int) -> (timing: time.Duration) {
	data := make([]u8, size)
	defer delete(data)

	for i in 0..<size {
		data[i] = u8('0' + i % 10)
	}
	data[idx] = 'z'

	accumulator: int

	watch: time.Stopwatch

	time.stopwatch_start(&watch)
	for _ in 0..<runs {
		accumulator += p(data, 'z')
	}
	time.stopwatch_stop(&watch)

	timing = time.stopwatch_duration(watch)

	log.debug(accumulator)
	return
}

bench_table :: proc(algo_name: string, forward: bool, plain: proc "contextless" ([]u8, byte) -> int, simd: proc "contextless" ([]u8, byte) -> int) {
	string_buffer := strings.builder_make()
	defer strings.builder_destroy(&string_buffer)

	tbl: table.Table
	table.init(&tbl)
	defer table.destroy(&tbl)

	// table.caption(&tbl, "index_byte benchmark")
	table.aligned_header_of_values(&tbl, .Right, "Algorithm", "Size", "Iterations", "Scalar", "SIMD", "SIMD Relative (%)", "SIMD Relative (x)")

	for size in sizes {
		needle_index := size - 1 if forward else 0

		plain_timing := run_trial_size(plain, size, needle_index, RUNS_PER_SIZE)
		simd_timing  := run_trial_size(simd,  size, needle_index, RUNS_PER_SIZE)

		_plain := fmt.tprintf("%8M",  plain_timing)
		_simd  := fmt.tprintf("%8M",  simd_timing)
		_relp  := fmt.tprintf("%.3f %%", f64(simd_timing) / f64(plain_timing) * 100.0)
		_relx  := fmt.tprintf("%.3f x",  1 / (f64(simd_timing) / f64(plain_timing)))

		table.aligned_row_of_values(
			&tbl,
			.Right,
			algo_name,
			size, RUNS_PER_SIZE, _plain, _simd, _relp, _relx)
	}

	builder_writer := strings.to_writer(&string_buffer)

	fmt.sbprintln(&string_buffer)
	table.write_plain_table(builder_writer, &tbl)

	my_table_string := strings.to_string(string_buffer)
	log.info(my_table_string)
}

@test
benchmark_index_byte :: proc(t: ^testing.T) {
	bench_table("index_byte",      true,  plain_index_byte,      bytes.index_byte)
	// bench_table("last_index_byte", false, plain_last_index_byte, bytes.last_index_byte)
}

/*
@test
benchmark_simd_index_hot :: proc(t: ^testing.T) {
	report: string
	for size in sizes {
		timing := run_trial_size(bytes.index_byte, size, size - 1, HOT, HOT)
		report = fmt.tprintf("%s\n        +++ % 8M | %v", report, size, timing)
		timing = run_trial_size(bytes.last_index_byte, size, 0, HOT, HOT)
		report = fmt.tprintf("%s\n (last) +++ % 8M | %v", report, size, timing)
	}
	log.info(report)
}
*/