Skip to content

Commit 434e857

Browse files
authored
Merge pull request #19 from MrZLeo/master
Improve benchmark support
2 parents d5037f8 + 3732583 commit 434e857

File tree

2 files changed

+131
-19
lines changed

2 files changed

+131
-19
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ optional = true
2121

2222
[dev-dependencies]
2323
criterion = "0.3"
24+
ctor = "0.1.23"
25+
rand = "0.8.5"
26+
rand_chacha = "0.3.1"
2427

2528
[[bench]]
2629
name = "memory_allocator_benchmark"

benches/memory_allocator_benchmark.rs

Lines changed: 128 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#[macro_use]
22
extern crate alloc;
3+
#[macro_use]
4+
extern crate ctor;
35

46
use std::sync::Arc;
57
use std::thread;
@@ -11,36 +13,57 @@ use alloc::alloc::Layout;
1113
use buddy_system_allocator::LockedHeap;
1214
use criterion::{black_box, criterion_group, criterion_main, Criterion};
1315

16+
const SMALL_SIZE: usize = 8;
17+
const LARGE_SIZE: usize = 1024 * 1024; // 1M
18+
const ALIGN: usize = 8;
19+
20+
/// Alloc small object
1421
#[inline]
15-
pub fn large_alloc<const ORDER: usize>(heap: &LockedHeap<ORDER>) {
16-
let layout = unsafe { Layout::from_size_align_unchecked(1024, 8) };
22+
pub fn small_alloc<const ORDER: usize>(heap: &LockedHeap<ORDER>) {
23+
let layout = unsafe { Layout::from_size_align_unchecked(SMALL_SIZE, ALIGN) };
1724
unsafe {
1825
let addr = heap.alloc(layout);
1926
heap.dealloc(addr, layout);
2027
}
2128
}
2229

30+
/// Alloc large object
2331
#[inline]
24-
pub fn small_alloc<const ORDER: usize>(heap: &LockedHeap<ORDER>) {
25-
let layout = unsafe { Layout::from_size_align_unchecked(8, 8) };
32+
pub fn large_alloc<const ORDER: usize>(heap: &LockedHeap<ORDER>) {
33+
let layout = unsafe { Layout::from_size_align_unchecked(LARGE_SIZE, ALIGN) };
2634
unsafe {
2735
let addr = heap.alloc(layout);
2836
heap.dealloc(addr, layout);
2937
}
3038
}
3139

40+
/// Multithreads alloc random sizes of object
3241
#[inline]
33-
pub fn mutil_thread_alloc<const ORDER: usize>(heap: &'static LockedHeap<ORDER>) {
34-
let mut threads = vec![];
42+
pub fn mutil_thread_random_size<const ORDER: usize>(heap: &'static LockedHeap<ORDER>) {
43+
const THREAD_SIZE: usize = 10;
44+
45+
use rand::prelude::*;
46+
use rand::{Rng, SeedableRng};
47+
use rand_chacha::ChaCha8Rng;
48+
49+
let mut threads = Vec::with_capacity(THREAD_SIZE);
3550
let alloc = Arc::new(heap);
36-
for i in 0..10 {
37-
let a = alloc.clone();
51+
for i in 0..THREAD_SIZE {
52+
let prethread_alloc = alloc.clone();
3853
let handle = thread::spawn(move || {
39-
let layout = unsafe { Layout::from_size_align_unchecked(i * 10, 8) };
40-
let addr;
41-
unsafe { addr = a.alloc(layout) }
42-
sleep(Duration::from_nanos(10 - i as u64));
43-
unsafe { a.dealloc(addr, layout) }
54+
// generate a random size of object use seed `i` to ensure the fixed
55+
// result of each turn
56+
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(i as u64);
57+
// generate a random object size in range of [SMALL_SIZE ..= LARGE_SIZE]
58+
let layout = unsafe {
59+
Layout::from_size_align_unchecked(rng.gen_range(SMALL_SIZE..=LARGE_SIZE), ALIGN)
60+
};
61+
let addr = unsafe { prethread_alloc.alloc(layout) };
62+
63+
// sleep for a while
64+
sleep(Duration::from_nanos((THREAD_SIZE - i) as u64));
65+
66+
unsafe { prethread_alloc.dealloc(addr, layout) }
4467
});
4568
threads.push(handle);
4669
}
@@ -51,32 +74,118 @@ pub fn mutil_thread_alloc<const ORDER: usize>(heap: &'static LockedHeap<ORDER>)
5174
}
5275
}
5376

77+
/// Multithread benchmark inspired by **Hoard** benchmark
78+
///
79+
/// Warning: This benchmark generally needs long time to finish
80+
///
81+
/// ----------------------------------------------------------------------
82+
/// Hoard: A Fast, Scalable, and Memory-Efficient Allocator
83+
/// for Shared-Memory Multiprocessors
84+
/// Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
85+
//
86+
/// Copyright (c) 1998-2000, The University of Texas at Austin.
87+
///
88+
/// This library is free software; you can redistribute it and/or modify
89+
/// it under the terms of the GNU Library General Public License as
90+
/// published by the Free Software Foundation, http://www.fsf.org.
91+
///
92+
/// This library is distributed in the hope that it will be useful, but
93+
/// WITHOUT ANY WARRANTY; without even the implied warranty of
94+
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
95+
/// Library General Public License for more details.
96+
/// ----------------------------------------------------------------------
97+
///
98+
#[inline]
99+
pub fn thread_test() {
100+
const N_ITERATIONS: usize = 50;
101+
const N_OBJECTS: usize = 30000;
102+
const N_THREADS: usize = 10;
103+
const OBJECT_SIZE: usize = 1;
104+
105+
#[derive(Clone)]
106+
struct Foo {
107+
pub a: i32,
108+
pub b: i32,
109+
}
110+
111+
let mut threads = Vec::with_capacity(N_THREADS);
112+
113+
for _i in 0..N_THREADS {
114+
let handle = thread::spawn(move || {
115+
// let a = new Foo * [nobjects / nthreads];
116+
let mut a = Vec::with_capacity(N_OBJECTS / N_THREADS);
117+
for j in 0..N_ITERATIONS {
118+
// inner object:
119+
// a[i] = new Foo[objSize];
120+
for k in 0..(N_OBJECTS / N_THREADS) {
121+
a.push(vec![
122+
Foo {
123+
a: k as i32,
124+
b: j as i32
125+
};
126+
OBJECT_SIZE
127+
]);
128+
129+
// in order to prevent optimization delete allocation directly
130+
// FIXME: don't know whether it works or not
131+
a[k][0].a += a[k][0].b;
132+
}
133+
}
134+
// auto drop here
135+
});
136+
threads.push(handle);
137+
}
138+
139+
for t in threads {
140+
t.join().unwrap();
141+
}
142+
}
143+
54144
const ORDER: usize = 32;
55-
static HEAP_ALLOCATOR: LockedHeap<ORDER> = LockedHeap::<ORDER>::new();
56-
const KERNEL_HEAP_SIZE: usize = 16 * 1024 * 1024;
57145
const MACHINE_ALIGN: usize = core::mem::size_of::<usize>();
146+
/// for now 128M is needed
147+
/// TODO: reduce memory use
148+
const KERNEL_HEAP_SIZE: usize = 128 * 1024 * 1024;
58149
const HEAP_BLOCK: usize = KERNEL_HEAP_SIZE / MACHINE_ALIGN;
59150
static mut HEAP: [usize; HEAP_BLOCK] = [0; HEAP_BLOCK];
60151

61-
pub fn criterion_benchmark(c: &mut Criterion) {
62-
// init heap
152+
/// Use `LockedHeap` as global allocator
153+
#[global_allocator]
154+
static HEAP_ALLOCATOR: LockedHeap<ORDER> = LockedHeap::<ORDER>::new();
155+
156+
/// Init heap
157+
///
158+
/// We need `ctor` here because benchmark is running behind the std enviroment,
159+
/// which means std will do some initialization before execute `fn main()`.
160+
/// However, our memory allocator must be init in runtime(use linkedlist, which
161+
/// can not be evaluated in compile time). And in the initialization phase, heap
162+
/// memory is needed.
163+
///
164+
/// So the solution in this dilemma is to run `fn init_heap()` in initialization phase
165+
/// rather than in `fn main()`. We need `ctor` to do this.
166+
#[ctor]
167+
fn init_heap() {
63168
let heap_start = unsafe { HEAP.as_ptr() as usize };
64169
unsafe {
65170
HEAP_ALLOCATOR
66171
.lock()
67172
.init(heap_start, HEAP_BLOCK * MACHINE_ALIGN);
68173
}
174+
}
69175

176+
/// Entry of benchmarks
177+
pub fn criterion_benchmark(c: &mut Criterion) {
70178
// run benchmark
71179
c.bench_function("small alloc", |b| {
72180
b.iter(|| small_alloc(black_box(&HEAP_ALLOCATOR)))
73181
});
74182
c.bench_function("large alloc", |b| {
75183
b.iter(|| large_alloc(black_box(&HEAP_ALLOCATOR)))
76184
});
77-
c.bench_function("mutil thread alloc", |b| {
78-
b.iter(|| mutil_thread_alloc(black_box(&HEAP_ALLOCATOR)))
185+
c.bench_function("mutil thread random size", |b| {
186+
b.iter(|| mutil_thread_random_size(black_box(&HEAP_ALLOCATOR)))
79187
});
188+
c.bench_function("threadtest", |b| b.iter(|| thread_test()));
80189
}
81190

82191
criterion_group!(benches, criterion_benchmark);

0 commit comments

Comments
 (0)