1
1
#[ macro_use]
2
2
extern crate alloc;
3
+ #[ macro_use]
4
+ extern crate ctor;
3
5
4
6
use std:: sync:: Arc ;
5
7
use std:: thread;
@@ -11,36 +13,57 @@ use alloc::alloc::Layout;
11
13
use buddy_system_allocator:: LockedHeap ;
12
14
use criterion:: { black_box, criterion_group, criterion_main, Criterion } ;
13
15
16
+ const SMALL_SIZE : usize = 8 ;
17
+ const LARGE_SIZE : usize = 1024 * 1024 ; // 1M
18
+ const ALIGN : usize = 8 ;
19
+
20
+ /// Alloc small object
14
21
#[ 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 ) } ;
17
24
unsafe {
18
25
let addr = heap. alloc ( layout) ;
19
26
heap. dealloc ( addr, layout) ;
20
27
}
21
28
}
22
29
30
+ /// Alloc large object
23
31
#[ 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 ) } ;
26
34
unsafe {
27
35
let addr = heap. alloc ( layout) ;
28
36
heap. dealloc ( addr, layout) ;
29
37
}
30
38
}
31
39
40
+ /// Multithreads alloc random sizes of object
32
41
#[ 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 ) ;
35
50
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 ( ) ;
38
53
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) }
44
67
} ) ;
45
68
threads. push ( handle) ;
46
69
}
@@ -51,32 +74,118 @@ pub fn mutil_thread_alloc<const ORDER: usize>(heap: &'static LockedHeap<ORDER>)
51
74
}
52
75
}
53
76
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
+
54
144
const ORDER : usize = 32 ;
55
- static HEAP_ALLOCATOR : LockedHeap < ORDER > = LockedHeap :: < ORDER > :: new ( ) ;
56
- const KERNEL_HEAP_SIZE : usize = 16 * 1024 * 1024 ;
57
145
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 ;
58
149
const HEAP_BLOCK : usize = KERNEL_HEAP_SIZE / MACHINE_ALIGN ;
59
150
static mut HEAP : [ usize ; HEAP_BLOCK ] = [ 0 ; HEAP_BLOCK ] ;
60
151
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 ( ) {
63
168
let heap_start = unsafe { HEAP . as_ptr ( ) as usize } ;
64
169
unsafe {
65
170
HEAP_ALLOCATOR
66
171
. lock ( )
67
172
. init ( heap_start, HEAP_BLOCK * MACHINE_ALIGN ) ;
68
173
}
174
+ }
69
175
176
+ /// Entry of benchmarks
177
+ pub fn criterion_benchmark ( c : & mut Criterion ) {
70
178
// run benchmark
71
179
c. bench_function ( "small alloc" , |b| {
72
180
b. iter ( || small_alloc ( black_box ( & HEAP_ALLOCATOR ) ) )
73
181
} ) ;
74
182
c. bench_function ( "large alloc" , |b| {
75
183
b. iter ( || large_alloc ( black_box ( & HEAP_ALLOCATOR ) ) )
76
184
} ) ;
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 ) ) )
79
187
} ) ;
188
+ c. bench_function ( "threadtest" , |b| b. iter ( || thread_test ( ) ) ) ;
80
189
}
81
190
82
191
criterion_group ! ( benches, criterion_benchmark) ;
0 commit comments