Skip to content

Commit

Permalink
v4.1 - counters, wasm benchmarks, docs, and my axe
Browse files Browse the repository at this point in the history
  • Loading branch information
SFBdragon committed Jan 23, 2024
1 parent 8b9aa0e commit 2c7ba93
Show file tree
Hide file tree
Showing 30 changed files with 1,921 additions and 1,448 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
target
Cargo.lock

pkg

*.asm
perf*
flamegraph.svg
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "talc"
version = "4.0.0"
version = "4.1.0"
rust-version = "1.67.1"
edition = "2021"
readme = "README.md"
Expand All @@ -20,13 +20,14 @@ exclude = [

[features]
fuzzing = []
counters = []
nightly_api = []
allocator = ["lock_api"]
default = ["allocator", "nightly_api"]
default = ["lock_api", "allocator", "nightly_api"]


[dependencies]
lock_api = { version= "0.4", optional = true, default-features = false }
lock_api = { version = "0.4", optional = true, default-features = false }

[dev-dependencies]
fastrand = "1.9"
Expand All @@ -35,7 +36,6 @@ linked_list_allocator = { version = "0.10", features = ["use_spin_nightly", "co
good_memory_allocator = { version = "0.1", features = ["spin", "allocator"] }
buddy-alloc = "0.5"
dlmalloc = { version = "0.2.4", default-features = false }
prev_talc = { package = "talc", version = "2" }

[profile.release]
lto = true
Expand Down
115 changes: 63 additions & 52 deletions README.md

Large diffs are not rendered by default.

40 changes: 24 additions & 16 deletions README_WASM.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,46 @@
# Talc on WASM

`Talc` provides a good middleground by being faster than `lol_alloc` and `dlmalloc` while inbetweening them in size, although your mileage will vary.

If you'd like to see comparisons to other allocators in this space, consider creating a pull request or opening an issue.
`Talc` provides a decent middleground by being faster than `lol_alloc` and `dlmalloc` (Rust WASM default) while inbetweening them in size. Although your mileage will vary, comparison tables are laid out below.

## Usage
Set the global allocator in your project after running `cargo add talc` as follows:

```rust
#[global_allocator] static TALC: talc::TalckWasm = unsafe { talc::TalckWasm::new_global() };
/// SAFETY:
/// The runtime environment must be single-threaded WASM.
///
/// Note: calls to memory.grow during use of the allocator is allowed.
#[global_allocator]
static ALLOCATOR: talc::TalckWasm = unsafe { talc::TalckWasm::new_global() };
```

Make sure that you have the `lock_api` feature enabled!
- e.g. using stable Rust, in your `Cargo.toml`: `talc = { version = "3", default-features = false, features = ["lock_api"] }`
Config features:
- If default features are disabled, make sure to enable `"lock_api"`.
- Turn on `"counters"` for allocation statistics accessible via `ALLOCATOR.lock().get_counters()`
- You can turn off default features to remove `"nightly_api"`, allowing stable Rust builds.

Note, this disables `nightly_api`, which isn't used in this API, allowing use of stable Rust.
`default-features = false, features = ["lock_api"]`

## Relative WASM Binary Size

Rough measurements of allocator size for relative comparison using `wasm_size.sh` and `wasm-size`.
Rough measurements of allocator size for relative comparison using `wasm-size.sh` + `/wasm-size`.

| Allocator | Size (bytes) - lower is better |
| --------- | ----- |
| lol_alloc | 18333 |
| talc | 22017 |
| dlmalloc | 23935 |
| lol_alloc | 15689 |
| talc | 19228 |
| dlmalloc (default) | 21316 |

## WASM Benchmarks

Rough allocator benchmarks for comparison from [this project](https://github.com/SFBdragon/wasm-alloc-bench).
Rough measurements of allocator speed for relative comparison using `wasm-bench.sh` + `/wasm-bench`.

| Allocator | Average Time per 100000 actions (ms) - lower is better |
|-----------|--------------|
| talc | 14.9 |
| dlmalloc | 17.6 |
| lol_alloc | 35.4 |
|-----------|-----|
| talc | 15.86 |
| dlmalloc (default) | 18.84 |
| lol_alloc | 34.26 |



If you'd like to see comparisons to other allocators in this space, consider creating a pull request or opening an issue.
11 changes: 9 additions & 2 deletions allocator_graphs.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,18 @@ def plot_benchmark(filename):
for k,v in data.items():
plt.plot(xaxis, v, label=k)
yvalues.append(v)

plt.legend()
test_name = filename[len(BENCHMARK_RESULTS_DIR): filename.find('.csv')]
plt.title(test_name)
test_name = filename[len(BENCHMARK_RESULTS_DIR): filename.find('.csv')]

if test_name == "random_actions":
plt.title("Random Actions Benchmark")
else:
plt.title(test_name)

plt.xlabel('time (seconds)\n')
plt.ylabel('score')
plt.gca().set_ylim(bottom=0)

full_diff_str = ''

Expand Down
Binary file modified benchmark_graphs/random_actions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 5 additions & 5 deletions benchmark_results/random_actions.csv
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
talc,2124135,3414161,5548655,6975498,8757172
dlmalloc,1751668,3010320,4297688,5617197,6864423
buddy_alloc,1857310,3287706,4827965,5416634,6440880
galloc,1499908,3021132,4449615,5866347,7214401
linked_list_allocator,721875,1268446,1744086,1501040,2072381
talc,2064640,3909176,5728649,7109421,8674172
dlmalloc,1839576,3271813,4653826,5833814,7605962
buddy_alloc,1832692,3029561,4986008,5546652,6890189
galloc,1462284,2881406,4347195,5752558,7009735
linked_list_allocator,744979,1212205,1185165,1455435,2023988
4 changes: 2 additions & 2 deletions buckets.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# modify these parameters to determine the bucketing strategy
# the main things we want are
# - coverage up to the 100MiB-1GiB area
# - minimize sizes per bucket
# - minimize number of allocation sizes per bucket
# - facilitate particularly quick allocation of small sizes
# - don't sacrifice the speed of large allocations much

Expand Down Expand Up @@ -36,7 +36,7 @@
for i, bsb in enumerate(range(word_buckets_limit, double_buckets_limit, 2*word_size)):
print("{1:>3}: {0:>8} {0:>20b} | ".format(bsb, i), end='\n')

print("log bins")
print("pseudo log-spaced bins")

b_ofst = int(math.log2(double_buckets_limit)) # log2_start_pow | 16
b_p2dv = int(math.log2(exp_fractions)) # log2_div_count | 4
Expand Down
62 changes: 25 additions & 37 deletions examples/macrobench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#![feature(const_mut_refs)]

use std::{
alloc::{GlobalAlloc, Layout},
io::Write,
alloc::{GlobalAlloc, Layout},
time::{Duration, Instant},
ptr::addr_of,
};

use buddy_alloc::{BuddyAllocParam, FastAllocParam, NonThreadsafeAlloc};
Expand All @@ -48,7 +49,7 @@ const BENCHMARK_RESULTS_DIR: &str = "./benchmark_results";
const TRIALS_AMOUNT: usize = 15;

struct NamedBenchmark {
benchmark_fn: fn(Duration, &dyn GlobalAlloc) -> usize,
benchmark_fn: fn(Duration, *const dyn GlobalAlloc) -> usize,
name: &'static str,
}

Expand All @@ -67,7 +68,7 @@ macro_rules! benchmark_list {

struct NamedAllocator {
name: &'static str,
init_fn: fn() -> &'static (dyn GlobalAlloc),
init_fn: fn() -> *const dyn GlobalAlloc,
}

macro_rules! allocator_list {
Expand All @@ -77,7 +78,7 @@ macro_rules! allocator_list {
NamedAllocator {
init_fn: $init_fn,
name: {
const INIT_FN_NAME:&'static str = stringify!($init_fn);
const INIT_FN_NAME: &'static str = stringify!($init_fn);
&INIT_FN_NAME["init_".len()..]
},
}
Expand All @@ -88,8 +89,6 @@ macro_rules! allocator_list {

static mut TALC_ALLOCATOR: talc::Talck<spin::Mutex<()>, talc::ErrOnOom> =
talc::Talc::new(talc::ErrOnOom).lock();
static mut PREV_TALC_ALLOCATOR: prev_talc::Talck<spin::Mutex<()>, prev_talc::ErrOnOom> =
prev_talc::Talc::new(prev_talc::ErrOnOom).lock();
static mut BUDDY_ALLOCATOR: buddy_alloc::NonThreadsafeAlloc = unsafe {
NonThreadsafeAlloc::new(
FastAllocParam::new(HEAP.as_ptr(), HEAP_SIZE / 8),
Expand Down Expand Up @@ -174,7 +173,6 @@ fn main() {

let allocators = allocator_list!(
init_talc,
init_prev_talc,
init_dlmalloc,
init_buddy_alloc,
init_galloc,
Expand Down Expand Up @@ -231,39 +229,30 @@ fn main() {
}
}

fn init_talc() -> &'static (dyn GlobalAlloc) {
fn init_talc() -> *const dyn GlobalAlloc {
unsafe {
TALC_ALLOCATOR = talc::Talc::new(talc::ErrOnOom).lock();
TALC_ALLOCATOR.lock().claim(HEAP.as_mut_slice().into()).unwrap();
&TALC_ALLOCATOR
addr_of!(TALC_ALLOCATOR)
}
}

#[allow(dead_code)]
fn init_prev_talc() -> &'static (dyn GlobalAlloc) {
unsafe {
PREV_TALC_ALLOCATOR =
prev_talc::Talc::with_arena(prev_talc::ErrOnOom, HEAP.as_mut_slice().into()).lock();
&PREV_TALC_ALLOCATOR
}
}

fn init_linked_list_allocator() -> &'static (dyn GlobalAlloc) {
fn init_linked_list_allocator() -> *const dyn GlobalAlloc {
let mut a = LINKED_LIST_ALLOCATOR.lock();
*a = linked_list_allocator::Heap::empty();
unsafe { a.init(HEAP.as_mut_ptr().cast(), HEAP_SIZE) }
&LINKED_LIST_ALLOCATOR
addr_of!(LINKED_LIST_ALLOCATOR)
}

fn init_galloc() -> &'static (dyn GlobalAlloc) {
fn init_galloc() -> *const dyn GlobalAlloc {
unsafe {
GALLOC_ALLOCATOR = good_memory_allocator::SpinLockedAllocator::empty();
GALLOC_ALLOCATOR.init(HEAP.as_ptr() as usize, HEAP_SIZE);
&GALLOC_ALLOCATOR
addr_of!(GALLOC_ALLOCATOR)
}
}

fn init_buddy_alloc() -> &'static (dyn GlobalAlloc) {
fn init_buddy_alloc() -> *const dyn GlobalAlloc {
unsafe {
BUDDY_ALLOCATOR = NonThreadsafeAlloc::new(
FastAllocParam::new(HEAP.as_ptr().cast(), HEAP.len() / 8),
Expand All @@ -273,21 +262,20 @@ fn init_buddy_alloc() -> &'static (dyn GlobalAlloc) {
64,
),
);

&BUDDY_ALLOCATOR
addr_of!(BUDDY_ALLOCATOR)
}
}

fn init_dlmalloc() -> &'static dyn GlobalAlloc {
fn init_dlmalloc() -> *const dyn GlobalAlloc {
unsafe {
DLMALLOC_ALLOCATOR = DlMallocator(lock_api::Mutex::new(
dlmalloc::Dlmalloc::new_with_allocator(DlmallocArena(spin::Mutex::new(false))),
));
&DLMALLOC_ALLOCATOR
addr_of!(DLMALLOC_ALLOCATOR)
}
}

pub fn random_actions(duration: Duration, allocator: &dyn GlobalAlloc) -> usize {
pub fn random_actions(duration: Duration, allocator: *const dyn GlobalAlloc) -> usize {
let mut score = 0;
let mut v = Vec::with_capacity(100000);

Expand Down Expand Up @@ -332,7 +320,7 @@ pub fn random_actions(duration: Duration, allocator: &dyn GlobalAlloc) -> usize
score
}

pub fn heap_efficiency(allocator: &dyn GlobalAlloc) -> f64 {
pub fn heap_efficiency(allocator: *const dyn GlobalAlloc) -> f64 {
let mut v = Vec::with_capacity(100000);
let mut used = 0;
let mut total = HEAP_SIZE;
Expand Down Expand Up @@ -388,16 +376,16 @@ pub fn heap_efficiency(allocator: &dyn GlobalAlloc) -> f64 {
used as f64 / total as f64 * 100.0
}

struct AllocationWrapper<'a> {
struct AllocationWrapper {
ptr: *mut u8,
layout: Layout,
allocator: &'a dyn GlobalAlloc,
allocator: *const dyn GlobalAlloc,
}
impl<'a> AllocationWrapper<'a> {
fn new(size: usize, align: usize, allocator: &'a dyn GlobalAlloc) -> Option<Self> {
impl AllocationWrapper {
fn new(size: usize, align: usize, allocator: *const dyn GlobalAlloc) -> Option<Self> {
let layout = Layout::from_size_align(size, align).unwrap();

let ptr = unsafe { allocator.alloc(layout) };
let ptr = unsafe { (*allocator).alloc(layout) };

if ptr.is_null() {
return None;
Expand All @@ -407,7 +395,7 @@ impl<'a> AllocationWrapper<'a> {
}

fn realloc(&mut self, new_size: usize) {
let new_ptr = unsafe { self.allocator.realloc(self.ptr, self.layout, new_size) };
let new_ptr = unsafe { (*self.allocator).realloc(self.ptr, self.layout, new_size) };
if new_ptr.is_null() {
return;
}
Expand All @@ -416,8 +404,8 @@ impl<'a> AllocationWrapper<'a> {
}
}

impl<'a> Drop for AllocationWrapper<'a> {
impl Drop for AllocationWrapper {
fn drop(&mut self) {
unsafe { self.allocator.dealloc(self.ptr, self.layout) }
unsafe { (*self.allocator).dealloc(self.ptr, self.layout) }
}
}
Loading

0 comments on commit 2c7ba93

Please sign in to comment.