Suboptimal, but working SOA support for XARR
This commit is contained in:
parent
cda4e1716b
commit
7d23ca691c
@ -296,6 +296,7 @@ main :: proc() {
|
|||||||
"odin",
|
"odin",
|
||||||
"build",
|
"build",
|
||||||
"game",
|
"game",
|
||||||
|
"-use-separate-modules",
|
||||||
"-define:NAME_STATIC_INIT=false",
|
"-define:NAME_STATIC_INIT=false",
|
||||||
"-define:RAYLIB_SHARED=true",
|
"-define:RAYLIB_SHARED=true",
|
||||||
"-define:PHYSFS_SHARED=true",
|
"-define:PHYSFS_SHARED=true",
|
||||||
|
@ -6,10 +6,11 @@ import "base:intrinsics"
|
|||||||
BASE_CHUNK_SIZE :: uint(64)
|
BASE_CHUNK_SIZE :: uint(64)
|
||||||
BASE_CHUNK_SIZE_LOG2 :: intrinsics.constant_log2(BASE_CHUNK_SIZE)
|
BASE_CHUNK_SIZE_LOG2 :: intrinsics.constant_log2(BASE_CHUNK_SIZE)
|
||||||
BASE_CHUNK_SHIFT :: BASE_CHUNK_SIZE_LOG2 - 1
|
BASE_CHUNK_SHIFT :: BASE_CHUNK_SIZE_LOG2 - 1
|
||||||
|
NUM_CHUNKS :: 30
|
||||||
|
|
||||||
Xarr :: struct($T: typeid) {
|
Xarr :: struct($T: typeid, $SOA := false) {
|
||||||
|
chunks: ([NUM_CHUNKS]#soa[]T when SOA else [NUM_CHUNKS][^]T),
|
||||||
len: int,
|
len: int,
|
||||||
chunks: [30][^]T,
|
|
||||||
allocated_chunks_mask: u32,
|
allocated_chunks_mask: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,10 +28,25 @@ chunk_size :: #force_inline proc "contextless" (chunk_idx: i8) -> uint {
|
|||||||
return BASE_CHUNK_SIZE << u32(max(chunk_idx - 1, 0))
|
return BASE_CHUNK_SIZE << u32(max(chunk_idx - 1, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
get_chunk_slice :: #force_inline proc "contextless" (a: $T/Xarr($E), chunk_idx: i8) -> []E {
|
get_chunk_slice_scalar :: #force_inline proc "contextless" (
|
||||||
|
a: $T/Xarr($E, false),
|
||||||
|
chunk_idx: i8,
|
||||||
|
) -> []E {
|
||||||
return a.chunks[chunk_idx][:chunk_size(chunk_idx)]
|
return a.chunks[chunk_idx][:chunk_size(chunk_idx)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_chunk_slice_soa :: #force_inline proc "contextless" (
|
||||||
|
a: $T/Xarr($E, true),
|
||||||
|
chunk_idx: i8,
|
||||||
|
) -> #soa[]E {
|
||||||
|
return a.chunks[chunk_idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
get_chunk_slice :: proc {
|
||||||
|
get_chunk_slice_scalar,
|
||||||
|
get_chunk_slice_soa,
|
||||||
|
}
|
||||||
|
|
||||||
capacity_from_allocated_mask :: #force_inline proc(allocated_mask: uint) -> uint {
|
capacity_from_allocated_mask :: #force_inline proc(allocated_mask: uint) -> uint {
|
||||||
return(
|
return(
|
||||||
(allocated_mask >> 1) << BASE_CHUNK_SIZE_LOG2 +
|
(allocated_mask >> 1) << BASE_CHUNK_SIZE_LOG2 +
|
||||||
@ -38,31 +54,36 @@ capacity_from_allocated_mask :: #force_inline proc(allocated_mask: uint) -> uint
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
capacity :: #force_inline proc(a: $T/Xarr($E)) -> u32 {
|
capacity :: #force_inline proc(a: $T/Xarr($E, $SOA)) -> u32 {
|
||||||
allocated_mask := a.allocated_chunks_mask
|
allocated_mask := a.allocated_chunks_mask
|
||||||
return capacity_from_allocated_mask(allocated_mask)
|
return capacity_from_allocated_mask(allocated_mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
reserve :: proc(a: $T/^Xarr($E), cap: int, allocator := context.allocator) {
|
reserve :: proc(a: $T/^Xarr($E, $SOA), cap: int, allocator := context.allocator) {
|
||||||
allocated_mask := a.allocated_chunks_mask
|
allocated_mask := a.allocated_chunks_mask
|
||||||
|
|
||||||
current_chunk := msb(allocated_mask)
|
current_chunk := msb(allocated_mask)
|
||||||
required_chunks := chunk_by_index(max(cap - 1, 0)) + 1
|
required_chunks := chunk_by_index(max(cap - 1, 0)) + 1
|
||||||
|
|
||||||
for i := current_chunk + 1; i < required_chunks; i += 1 {
|
for i := current_chunk + 1; i < required_chunks; i += 1 {
|
||||||
chunk_slice := make([]E, chunk_size(i), allocator)
|
when SOA {
|
||||||
a.chunks[i] = raw_data(chunk_slice)
|
chunk_slice := make_soa_slice(#soa[]E, chunk_size(i), allocator)
|
||||||
|
a.chunks[i] = chunk_slice
|
||||||
|
} else {
|
||||||
|
chunk_slice := make([]E, chunk_size(i), allocator)
|
||||||
|
a.chunks[i] = raw_data(chunk_slice)
|
||||||
|
}
|
||||||
a.allocated_chunks_mask |= u32(1) << u8(i)
|
a.allocated_chunks_mask |= u32(1) << u8(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
append :: proc(a: $T/^Xarr($E), elems: ..E, allocator := context.allocator) {
|
append :: proc(a: $T/^Xarr($E, $SOA), elems: ..E, allocator := context.allocator) {
|
||||||
if len(elems) == 0 {
|
if len(elems) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
reserve(a, a.len + len(elems))
|
reserve(a, a.len + len(elems))
|
||||||
set_elems_assume_allocated(a^, elems)
|
set_elems_assume_allocated(a, elems)
|
||||||
a.len += len(elems)
|
a.len += len(elems)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +102,7 @@ translate_index :: #force_inline proc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
set_elems_assume_allocated :: proc(a: $T/Xarr($E), elems: []E) {
|
set_elems_assume_allocated :: proc(a: $T/^Xarr($E, $SOA), elems: []E) {
|
||||||
for &e, i in elems {
|
for &e, i in elems {
|
||||||
idx := a.len + i
|
idx := a.len + i
|
||||||
chunk_idx, idx_within_chunk := translate_index(idx)
|
chunk_idx, idx_within_chunk := translate_index(idx)
|
||||||
@ -91,55 +112,55 @@ set_elems_assume_allocated :: proc(a: $T/Xarr($E), elems: []E) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
set :: proc(a: $T/Xarr($E), #any_int idx: int, val: E) {
|
set :: proc(a: $T/Xarr($E, $SOA), #any_int idx: int, val: E) {
|
||||||
assert(idx >= 0 && idx < a.len)
|
assert(idx >= 0 && idx < a.len)
|
||||||
chunk_idx, idx_within_chunk := translate_index(idx)
|
chunk_idx, idx_within_chunk := translate_index(idx)
|
||||||
return get_chunk_slice(a, chunk_idx)[idx_within_chunk]
|
return get_chunk_slice(a, chunk_idx)[idx_within_chunk]
|
||||||
}
|
}
|
||||||
|
|
||||||
get :: proc(a: $T/Xarr($E), #any_int idx: int) -> E {
|
get :: proc(a: $T/Xarr($E, $SOA), #any_int idx: int) -> E {
|
||||||
assert(idx >= 0 && idx < a.len)
|
assert(idx >= 0 && idx < a.len)
|
||||||
|
|
||||||
chunk_idx, idx_within_chunk := translate_index(idx)
|
chunk_idx, idx_within_chunk := translate_index(idx)
|
||||||
return get_chunk_slice(a, chunk_idx)[idx_within_chunk]
|
return get_chunk_slice(a, chunk_idx)[idx_within_chunk]
|
||||||
}
|
}
|
||||||
|
|
||||||
get_ptr :: proc(a: $T/Xarr($E), #any_int idx: int) -> ^E {
|
get_ptr :: proc(a: $T/Xarr($E, $SOA), #any_int idx: int) -> ^E {
|
||||||
assert(idx >= 0 && idx < a.len)
|
assert(idx >= 0 && idx < a.len)
|
||||||
|
|
||||||
chunk_idx, idx_within_chunk := translate_index(idx)
|
chunk_idx, idx_within_chunk := translate_index(idx)
|
||||||
return &get_chunk_slice(a, chunk_idx)[idx_within_chunk]
|
return &get_chunk_slice(a, chunk_idx)[idx_within_chunk]
|
||||||
}
|
}
|
||||||
|
|
||||||
unordered_remove :: proc(a: $T/^Xarr($E), #any_int idx: int) {
|
unordered_remove :: proc(a: $T/^Xarr($E, $SOA), #any_int idx: int) {
|
||||||
assert(idx >= 0 && idx < a.len)
|
assert(idx >= 0 && idx < a.len)
|
||||||
|
|
||||||
get_ptr(a^, idx)^ = get(a^, a.len - 1)
|
get_ptr(a^, idx)^ = get(a^, a.len - 1)
|
||||||
a.len -= 1
|
a.len -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
clear :: proc "contextless" (a: $T/^Xarr($E)) {
|
clear :: proc "contextless" (a: $T/^Xarr($E, $SOA)) {
|
||||||
a.len = 0
|
a.len = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
delete :: proc(a: $T/^Xarr($E), allocator := context.allocator) {
|
delete :: proc(a: $T/^Xarr($E, $SOA), allocator := context.allocator) {
|
||||||
for i in 0 ..< len(a.chunks) {
|
for i in 0 ..< len(a.chunks) {
|
||||||
builtin.delete(get_chunk_slice(a^, i8(i)), allocator)
|
builtin.delete(get_chunk_slice(a^, i8(i)), allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
a^ = Xarr(E){}
|
a^ = Xarr(E, SOA){}
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator :: struct($E: typeid) {
|
Iterator :: struct($E: typeid, $SOA: bool) {
|
||||||
xarr: ^Xarr(E),
|
xarr: ^Xarr(E, SOA),
|
||||||
idx: int,
|
idx: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator :: proc(a: $T/^Xarr($E), start_idx := 0) -> Iterator(E) {
|
iterator :: proc(a: $T/^Xarr($E, $SOA), start_idx := 0) -> Iterator(E, SOA) {
|
||||||
return Iterator(E){xarr = a, idx = start_idx}
|
return Iterator(E, SOA){xarr = a, idx = start_idx}
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator_next :: proc(it: ^Iterator($E)) -> (e: ^E, idx: int, ok: bool) {
|
iterator_next :: proc(it: ^Iterator($E, $SOA)) -> (e: ^E, idx: int, ok: bool) {
|
||||||
if it.idx >= it.xarr.len {
|
if it.idx >= it.xarr.len {
|
||||||
return nil, it.idx, false
|
return nil, it.idx, false
|
||||||
}
|
}
|
||||||
|
@ -120,3 +120,18 @@ test_iterator :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, e^, i)
|
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})
|
||||||
|
testing.expect_value(t, size_of(Xarr(My_Struct, false)), 0)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user