This is a workaround for a lack of an ability to tell TSan that it
should clear any state it has about certain memory ranges to prevent
false positives.
This is a total rewrite after having experimented with a variety of
techniques including a dual-allocator strategy for small and large size
classes where the large class allocator had a coalescing mechanism.
This allocator design is much closer to `mimalloc` in spirit and
performs almost twice as fast as the original bitmap-based design.
Additionally, several bugs have been fixed. The most important one being
the lock-free synchronization method used for remote frees. This design
uses a single atomic pointer that is doubly-tagged to pass remote frees
either to the heap or to the slab, depending on the ownership status.
Previously, a wide slab could be freed during this phase which could
trigger the superpage itself to be freed, resulting in iteration across
potentially invalid memory if the superpage was returned to the host and
not placed in the orphanage.
`heap_free_wide_slab` has had its responsibilities reduced to just
freeing the wide slab, and `heap_free_superpage_if_empty_and_unused` has
been added to better clarify what is happening where.
This should be more robust during merges that happen simultaneously with
remote frees.
Previously, we would preserve the cache entry, but there is an edge case
where a slab of a lower index could be remotely freed and missed during
a merge.
- Simplify the malloc-based allocator
- Correct the type signatures on malloc/calloc/realloc
- Make `heap_alloc/heap_free/heap_resize` API consistent across platforms
- Add the test bench for the allocator
- Move old allocator code to the test bench
- Fix `heap_resize` usage in `os2/env_linux.odin` to fit new API
requiring `old_size`
The new reduce_add/reduce_mul procs perform the corresponding arithmetic
reduction in different orders than sequential order. These alternative
orders can often offer better SIMD hardware utilization.
Two different orders are added: pair-wise (operating on pairs of
adjacent elements) or bisection-wise (operating element-wise on the
first and last N/2 elements of the vector).