448 lines
9.7 KiB
Odin
448 lines
9.7 KiB
Odin
package xarr
|
|
|
|
import "base:runtime"
|
|
import "core:fmt"
|
|
import "core:mem/virtual"
|
|
import "core:strings"
|
|
import "core:testing"
|
|
import "core:time"
|
|
|
|
@(test)
|
|
test_msb :: proc(t: ^testing.T) {
|
|
testing.expect_value(t, msb(0), -1)
|
|
testing.expect_value(t, msb(1), 0)
|
|
testing.expect_value(t, msb(2), 1)
|
|
testing.expect_value(t, msb(3), 1)
|
|
testing.expect_value(t, msb(4), 2)
|
|
testing.expect_value(t, msb(5), 2)
|
|
testing.expect_value(t, msb(6), 2)
|
|
testing.expect_value(t, msb(7), 2)
|
|
testing.expect_value(t, msb(8), 3)
|
|
testing.expect_value(t, msb(16), 4)
|
|
testing.expect_value(t, msb(64), 6)
|
|
}
|
|
|
|
@(test)
|
|
test_chunk_sizes :: proc(t: ^testing.T) {
|
|
testing.expect_value(t, chunk_size(0), BASE_CHUNK_SIZE)
|
|
testing.expect_value(t, chunk_size(1), BASE_CHUNK_SIZE)
|
|
testing.expect_value(t, chunk_size(2), BASE_CHUNK_SIZE * 2)
|
|
testing.expect_value(t, chunk_size(3), BASE_CHUNK_SIZE * 4)
|
|
testing.expect_value(t, chunk_size(4), BASE_CHUNK_SIZE * 8)
|
|
}
|
|
|
|
@(test)
|
|
test_capacity_from_mask :: proc(t: ^testing.T) {
|
|
testing.expect_value(t, capacity_from_allocated_mask(0b1), chunk_size(0))
|
|
testing.expect_value(t, capacity_from_allocated_mask(0b11), chunk_size(0) + chunk_size(1))
|
|
testing.expect_value(
|
|
t,
|
|
capacity_from_allocated_mask(0b111),
|
|
chunk_size(0) + chunk_size(1) + chunk_size(2),
|
|
)
|
|
testing.expect_value(
|
|
t,
|
|
capacity_from_allocated_mask(0b1111),
|
|
chunk_size(0) + chunk_size(1) + chunk_size(2) + chunk_size(3),
|
|
)
|
|
testing.expect_value(
|
|
t,
|
|
capacity_from_allocated_mask(0b11111),
|
|
chunk_size(0) + chunk_size(1) + chunk_size(2) + chunk_size(3) + chunk_size(4),
|
|
)
|
|
}
|
|
|
|
@(test)
|
|
test_indexing :: proc(t: ^testing.T) {
|
|
chunk, idx := translate_index(0)
|
|
testing.expect_value(t, chunk, 0)
|
|
testing.expect_value(t, idx, 0)
|
|
|
|
chunk, idx = translate_index(BASE_CHUNK_SIZE - 1)
|
|
testing.expect_value(t, chunk, 0)
|
|
testing.expect_value(t, idx, BASE_CHUNK_SIZE - 1)
|
|
|
|
chunk, idx = translate_index(BASE_CHUNK_SIZE)
|
|
testing.expect_value(t, chunk, 1)
|
|
testing.expect_value(t, idx, 0)
|
|
|
|
chunk, idx = translate_index(BASE_CHUNK_SIZE * 3 - 1)
|
|
testing.expect_value(t, chunk, 2)
|
|
testing.expect_value(t, idx, BASE_CHUNK_SIZE - 1)
|
|
|
|
chunk, idx = translate_index(BASE_CHUNK_SIZE * 5)
|
|
testing.expect_value(t, chunk, 3)
|
|
testing.expect_value(t, idx, BASE_CHUNK_SIZE)
|
|
}
|
|
|
|
@(test)
|
|
test_basic :: proc(t: ^testing.T) {
|
|
a: Xarr(int)
|
|
defer delete(&a)
|
|
|
|
NUM :: 10000
|
|
RUNS :: 4
|
|
|
|
for _ in 0 ..< RUNS {
|
|
defer clear(&a)
|
|
|
|
for i in 0 ..< NUM {
|
|
append(&a, i)
|
|
}
|
|
|
|
testing.expect_value(t, a.len, NUM)
|
|
|
|
for i in 0 ..< NUM {
|
|
testing.expect_value(t, get(a, i), i)
|
|
}
|
|
}
|
|
}
|
|
|
|
@(test)
|
|
test_remove :: proc(t: ^testing.T) {
|
|
a: Xarr(int)
|
|
defer delete(&a)
|
|
|
|
append(&a, 1, 2, 3, 4)
|
|
|
|
unordered_remove(&a, 1)
|
|
|
|
testing.expect_value(t, a.len, 3)
|
|
testing.expect_value(t, get(a, 0), 1)
|
|
testing.expect_value(t, get(a, 1), 4)
|
|
testing.expect_value(t, get(a, 2), 3)
|
|
}
|
|
|
|
@(test)
|
|
test_iterator :: proc(t: ^testing.T) {
|
|
a: Xarr(int)
|
|
defer delete(&a)
|
|
|
|
append(&a, 0, 1, 2, 3, 4)
|
|
|
|
it := iterator(&a)
|
|
for e, i in iterator_next(&it) {
|
|
testing.expect_value(t, e^, i)
|
|
}
|
|
}
|
|
|
|
@(test)
|
|
test_soa :: proc(t: ^testing.T) {
|
|
My_Struct :: struct {
|
|
x, y, z: f32,
|
|
}
|
|
a: Xarr(My_Struct, true)
|
|
defer delete(&a)
|
|
|
|
append(&a, My_Struct{x = 1, y = 2, z = 3})
|
|
|
|
testing.expect_value(t, get(a, 0), My_Struct{x = 1, y = 2, z = 3})
|
|
}
|
|
|
|
@(test)
|
|
benchmark_dyn_array_append :: proc(t: ^testing.T) {
|
|
str: strings.Builder
|
|
strings.builder_init(&str, context.allocator)
|
|
defer {
|
|
fmt.println(strings.to_string(str))
|
|
strings.builder_destroy(&str)
|
|
}
|
|
|
|
|
|
{
|
|
arena: virtual.Arena
|
|
arena_err := virtual.arena_init_static(&arena)
|
|
testing.expect_value(t, arena_err, nil)
|
|
defer virtual.arena_destroy(&arena)
|
|
|
|
name := "Dynamic Array Append"
|
|
options := &time.Benchmark_Options {
|
|
rounds = 10_000,
|
|
bytes = 100_000,
|
|
setup = setup_bench,
|
|
bench = benchmark_dynamic_array_append,
|
|
// teardown = teardown_bench,
|
|
}
|
|
err := time.benchmark(options, virtual.arena_allocator(&arena))
|
|
testing.expect_value(t, err, nil)
|
|
benchmark_print(&str, name, options)
|
|
}
|
|
}
|
|
|
|
@(test)
|
|
benchmark_xarr_append :: proc(t: ^testing.T) {
|
|
str: strings.Builder
|
|
strings.builder_init(&str, context.allocator)
|
|
defer {
|
|
fmt.println(strings.to_string(str))
|
|
strings.builder_destroy(&str)
|
|
}
|
|
|
|
{
|
|
arena: virtual.Arena
|
|
arena_err := virtual.arena_init_static(&arena)
|
|
testing.expect_value(t, arena_err, nil)
|
|
defer virtual.arena_destroy(&arena)
|
|
|
|
name := "Xarr Append"
|
|
options := &time.Benchmark_Options {
|
|
rounds = 10_000,
|
|
bytes = 1_000_000,
|
|
setup = setup_bench,
|
|
bench = benchmar_xarr_append,
|
|
}
|
|
err := time.benchmark(options, virtual.arena_allocator(&arena))
|
|
testing.expect_value(t, err, nil)
|
|
benchmark_print(&str, name, options)
|
|
}
|
|
}
|
|
|
|
ITERATION_ARRAY_NUM :: 1_000_000
|
|
ITERATION_ROUNDS :: 10_000
|
|
|
|
@(test)
|
|
benchmark_xarr_index_iteration :: proc(t: ^testing.T) {
|
|
str: strings.Builder
|
|
strings.builder_init(&str, context.allocator)
|
|
defer {
|
|
fmt.println(strings.to_string(str))
|
|
strings.builder_destroy(&str)
|
|
}
|
|
|
|
arr: Xarr(int)
|
|
defer delete(&arr)
|
|
|
|
for i in 0 ..< ITERATION_ARRAY_NUM {
|
|
append(&arr, i)
|
|
}
|
|
|
|
total_sum: int
|
|
|
|
diff: time.Duration
|
|
{
|
|
time.SCOPED_TICK_DURATION(&diff)
|
|
|
|
for _ in 0 ..< ITERATION_ROUNDS {
|
|
|
|
sum: int
|
|
for i in 0 ..< arr.len {
|
|
sum += get(arr, i)
|
|
}
|
|
|
|
total_sum += sum
|
|
}
|
|
}
|
|
|
|
options := &time.Benchmark_Options{rounds = 10_000, bytes = 1_000_000}
|
|
options.count = options.rounds
|
|
options.processed = size_of(int) * arr.len * options.rounds
|
|
|
|
options.duration = diff
|
|
times_per_second := f64(time.Second) / f64(diff)
|
|
options.rounds_per_second = times_per_second * f64(options.count)
|
|
options.megabytes_per_second = f64(options.processed) / f64(1024 * 1024) * times_per_second
|
|
|
|
benchmark_print(&str, "Xarr Index Iteration", options)
|
|
}
|
|
|
|
@(test)
|
|
benchmark_xarr_chunk_iteration :: proc(t: ^testing.T) {
|
|
str: strings.Builder
|
|
strings.builder_init(&str, context.allocator)
|
|
defer {
|
|
fmt.println(strings.to_string(str))
|
|
strings.builder_destroy(&str)
|
|
}
|
|
|
|
arr: Xarr(int)
|
|
defer delete(&arr)
|
|
|
|
for i in 0 ..< ITERATION_ARRAY_NUM {
|
|
append(&arr, i)
|
|
}
|
|
|
|
total_sum: int
|
|
|
|
diff: time.Duration
|
|
{
|
|
time.SCOPED_TICK_DURATION(&diff)
|
|
|
|
for _ in 0 ..< ITERATION_ROUNDS {
|
|
sum: int
|
|
|
|
it := chunk_iterator(&arr)
|
|
for chunk, base_idx in chunk_iterator_next(&it) {
|
|
for i in 0 ..< len(chunk) {
|
|
sum += chunk[i]
|
|
}
|
|
}
|
|
|
|
total_sum += sum
|
|
}
|
|
}
|
|
|
|
options := &time.Benchmark_Options{rounds = 10_000, bytes = 1_000_000}
|
|
options.count = options.rounds
|
|
options.processed = size_of(int) * arr.len * options.rounds
|
|
|
|
options.duration = diff
|
|
times_per_second := f64(time.Second) / f64(diff)
|
|
options.rounds_per_second = times_per_second * f64(options.count)
|
|
options.megabytes_per_second = f64(options.processed) / f64(1024 * 1024) * times_per_second
|
|
|
|
benchmark_print(&str, "Xarr Chunk Iteration", options)
|
|
}
|
|
|
|
@(test)
|
|
benchmark_slice_index_iteration :: proc(t: ^testing.T) {
|
|
str: strings.Builder
|
|
strings.builder_init(&str, context.allocator)
|
|
defer {
|
|
fmt.println(strings.to_string(str))
|
|
strings.builder_destroy(&str)
|
|
}
|
|
|
|
slice := make([]int, ITERATION_ARRAY_NUM)
|
|
|
|
for i in 0 ..< ITERATION_ARRAY_NUM {
|
|
slice[i] = i
|
|
}
|
|
|
|
total_sum: int
|
|
|
|
diff: time.Duration
|
|
{
|
|
time.SCOPED_TICK_DURATION(&diff)
|
|
|
|
for _ in 0 ..< ITERATION_ROUNDS {
|
|
|
|
sum: int
|
|
for i in 0 ..< len(slice) {
|
|
sum += slice[i]
|
|
}
|
|
|
|
total_sum += sum
|
|
}
|
|
}
|
|
|
|
options := &time.Benchmark_Options{rounds = 10_000, bytes = 1_000_000}
|
|
options.count = options.rounds
|
|
options.processed = size_of(int) * len(slice) * options.rounds
|
|
|
|
options.duration = diff
|
|
times_per_second := f64(time.Second) / f64(diff)
|
|
options.rounds_per_second = times_per_second * f64(options.count)
|
|
options.megabytes_per_second = f64(options.processed) / f64(1024 * 1024) * times_per_second
|
|
|
|
benchmark_print(&str, "Slice Index Iteration", options)
|
|
}
|
|
|
|
setup_bench :: proc(
|
|
options: ^time.Benchmark_Options,
|
|
allocator := context.allocator,
|
|
) -> (
|
|
err: time.Benchmark_Error,
|
|
) {
|
|
assert(options != nil)
|
|
|
|
options.input = make([]u8, options.bytes, allocator)
|
|
for &b, i in options.input {
|
|
b = u8(i & 0xff)
|
|
}
|
|
return nil if len(options.input) == options.bytes else .Allocation_Error
|
|
}
|
|
|
|
teardown_bench :: proc(
|
|
options: ^time.Benchmark_Options,
|
|
allocator := context.allocator,
|
|
) -> (
|
|
err: time.Benchmark_Error,
|
|
) {
|
|
assert(options != nil)
|
|
|
|
runtime.delete(options.input)
|
|
return nil
|
|
}
|
|
|
|
benchmark_dynamic_array_append :: proc(
|
|
options: ^time.Benchmark_Options,
|
|
allocator := context.allocator,
|
|
) -> (
|
|
err: time.Benchmark_Error,
|
|
) {
|
|
buf := options.input
|
|
|
|
for _ in 0 ..< options.rounds {
|
|
arr: [dynamic]u8
|
|
defer runtime.delete(arr)
|
|
|
|
for byte in buf {
|
|
runtime.append(&arr, byte)
|
|
}
|
|
}
|
|
options.count = options.rounds
|
|
options.processed = options.rounds * options.bytes
|
|
|
|
return nil
|
|
}
|
|
|
|
benchmar_xarr_append :: proc(
|
|
options: ^time.Benchmark_Options,
|
|
allocator := context.allocator,
|
|
) -> (
|
|
err: time.Benchmark_Error,
|
|
) {
|
|
buf := options.input
|
|
|
|
for _ in 0 ..< options.rounds {
|
|
arr: Xarr(u8)
|
|
defer delete(&arr)
|
|
|
|
for byte in buf {
|
|
append(&arr, byte)
|
|
}
|
|
}
|
|
options.count = options.rounds
|
|
options.processed = options.rounds * options.bytes
|
|
return nil
|
|
}
|
|
|
|
benchmar_xarr_iterate :: proc(
|
|
options: ^time.Benchmark_Options,
|
|
allocator := context.allocator,
|
|
) -> (
|
|
err: time.Benchmark_Error,
|
|
) {
|
|
buf := options.input
|
|
|
|
for _ in 0 ..< options.rounds {
|
|
arr: Xarr(u8)
|
|
defer delete(&arr)
|
|
|
|
for byte in buf {
|
|
append(&arr, byte)
|
|
}
|
|
}
|
|
options.count = options.rounds
|
|
options.processed = options.rounds * options.bytes
|
|
return nil
|
|
}
|
|
|
|
benchmark_print :: proc(
|
|
str: ^strings.Builder,
|
|
name: string,
|
|
options: ^time.Benchmark_Options,
|
|
loc := #caller_location,
|
|
) {
|
|
fmt.sbprintfln(
|
|
str,
|
|
"[%v] %v rounds, %v bytes processed in %v ns\n\t\t%5.3f rounds/s, %5.3f MiB/s\n",
|
|
name,
|
|
options.rounds,
|
|
options.processed,
|
|
time.duration_nanoseconds(options.duration),
|
|
options.rounds_per_second,
|
|
options.megabytes_per_second,
|
|
)
|
|
}
|