2
2
extern crate criterion;
3
3
4
4
use criterion:: { black_box, Criterion } ;
5
- use rand:: rng;
6
- use rand_distr:: { Distribution , Normal , Uniform } ;
7
- use sieve_cache:: SieveCache ;
5
+ use rand:: prelude:: * ;
6
+ use rand:: thread_rng;
7
+ use rand_distr:: { Distribution , Normal } ;
8
+ use sieve_cache:: { ShardedSieveCache , SieveCache , SyncSieveCache } ;
9
+ use std:: sync:: Arc ;
10
+ use std:: thread;
8
11
9
12
fn bench_sequence ( c : & mut Criterion ) {
10
13
c. bench_function ( "bench_sequence" , |b| {
@@ -27,18 +30,18 @@ fn bench_sequence(c: &mut Criterion) {
27
30
fn bench_composite ( c : & mut Criterion ) {
28
31
c. bench_function ( "bench_composite" , |b| {
29
32
let mut cache: SieveCache < u64 , ( Vec < u8 > , u64 ) > = SieveCache :: new ( 68 ) . unwrap ( ) ;
30
- let mut rng = rng ( ) ;
31
- let uniform = Uniform :: new ( 0 , 100 ) . unwrap ( ) ;
32
- let mut rand_iter = uniform. sample_iter ( & mut rng) ;
33
+ let mut rng = thread_rng ( ) ;
34
+
33
35
b. iter ( || {
34
36
for _ in 1 ..1000 {
35
- let n = rand_iter . next ( ) . unwrap ( ) ;
37
+ let n = rng . gen_range ( 0 .. 100 ) ;
36
38
black_box ( cache. insert ( n, ( vec ! [ 0u8 ; 12 ] , n) ) ) ;
37
39
}
38
40
} ) ;
41
+
39
42
b. iter ( || {
40
43
for _ in 1 ..1000 {
41
- let n = rand_iter . next ( ) . unwrap ( ) ;
44
+ let n = rng . gen_range ( 0 .. 100 ) ;
42
45
black_box ( cache. get ( & n) ) ;
43
46
}
44
47
} ) ;
@@ -53,28 +56,131 @@ fn bench_composite_normal(c: &mut Criterion) {
53
56
let mut cache: SieveCache < u64 , ( Vec < u8 > , u64 ) > = SieveCache :: new ( SIGMA as usize ) . unwrap ( ) ;
54
57
55
58
// This should roughly cover all elements (within 3-sigma)
56
- let mut rng = rng ( ) ;
59
+ let mut rng = thread_rng ( ) ;
57
60
let normal = Normal :: new ( 50.0 , SIGMA ) . unwrap ( ) ;
58
- let mut rand_iter = normal . sample_iter ( & mut rng ) . map ( |x| ( x as u64 ) % 100 ) ;
61
+
59
62
b. iter ( || {
60
63
for _ in 1 ..1000 {
61
- let n = rand_iter. next ( ) . unwrap ( ) ;
64
+ let sample = normal. sample ( & mut rng) ;
65
+ let n = ( sample as u64 ) % 100 ;
62
66
black_box ( cache. insert ( n, ( vec ! [ 0u8 ; 12 ] , n) ) ) ;
63
67
}
64
68
} ) ;
69
+
65
70
b. iter ( || {
66
71
for _ in 1 ..1000 {
67
- let n = rand_iter. next ( ) . unwrap ( ) ;
72
+ let sample = normal. sample ( & mut rng) ;
73
+ let n = ( sample as u64 ) % 100 ;
68
74
black_box ( cache. get ( & n) ) ;
69
75
}
70
76
} ) ;
71
77
} ) ;
72
78
}
73
79
80
+ // Benchmark to compare thread-safe implementations in high-concurrency scenario
81
+ fn bench_concurrent_access ( c : & mut Criterion ) {
82
+ let mut group = c. benchmark_group ( "concurrent_access" ) ;
83
+
84
+ // Set up benchmark parameters
85
+ const CACHE_SIZE : usize = 10000 ;
86
+ const NUM_THREADS : usize = 8 ;
87
+ const OPS_PER_THREAD : usize = 1000 ;
88
+
89
+ // Benchmark with SyncSieveCache (single mutex)
90
+ group. bench_function ( "sync_cache" , |b| {
91
+ b. iter ( || {
92
+ let cache = Arc :: new ( SyncSieveCache :: new ( CACHE_SIZE ) . unwrap ( ) ) ;
93
+ let mut handles = Vec :: with_capacity ( NUM_THREADS ) ;
94
+
95
+ for _ in 0 ..NUM_THREADS {
96
+ let cache_clone = Arc :: clone ( & cache) ;
97
+ let handle = thread:: spawn ( move || {
98
+ let mut rng = thread_rng ( ) ;
99
+
100
+ for _ in 0 ..OPS_PER_THREAD {
101
+ let key = rng. gen_range ( 0 ..1000 ) ;
102
+ if rng. gen :: < bool > ( ) {
103
+ black_box ( cache_clone. insert ( key, key) ) ;
104
+ } else {
105
+ black_box ( cache_clone. get ( & key) ) ;
106
+ }
107
+ }
108
+ } ) ;
109
+ handles. push ( handle) ;
110
+ }
111
+
112
+ for handle in handles {
113
+ handle. join ( ) . unwrap ( ) ;
114
+ }
115
+ } ) ;
116
+ } ) ;
117
+
118
+ // Benchmark with ShardedSieveCache (multiple mutexes)
119
+ group. bench_function ( "sharded_cache" , |b| {
120
+ b. iter ( || {
121
+ let cache = Arc :: new ( ShardedSieveCache :: new ( CACHE_SIZE ) . unwrap ( ) ) ;
122
+ let mut handles = Vec :: with_capacity ( NUM_THREADS ) ;
123
+
124
+ for _ in 0 ..NUM_THREADS {
125
+ let cache_clone = Arc :: clone ( & cache) ;
126
+ let handle = thread:: spawn ( move || {
127
+ let mut rng = thread_rng ( ) ;
128
+
129
+ for _ in 0 ..OPS_PER_THREAD {
130
+ let key = rng. gen_range ( 0 ..1000 ) ;
131
+ if rng. gen :: < bool > ( ) {
132
+ black_box ( cache_clone. insert ( key, key) ) ;
133
+ } else {
134
+ black_box ( cache_clone. get ( & key) ) ;
135
+ }
136
+ }
137
+ } ) ;
138
+ handles. push ( handle) ;
139
+ }
140
+
141
+ for handle in handles {
142
+ handle. join ( ) . unwrap ( ) ;
143
+ }
144
+ } ) ;
145
+ } ) ;
146
+
147
+ // Benchmark with different shard counts
148
+ group. bench_function ( "sharded_cache_32_shards" , |b| {
149
+ b. iter ( || {
150
+ let cache = Arc :: new ( ShardedSieveCache :: with_shards ( CACHE_SIZE , 32 ) . unwrap ( ) ) ;
151
+ let mut handles = Vec :: with_capacity ( NUM_THREADS ) ;
152
+
153
+ for _ in 0 ..NUM_THREADS {
154
+ let cache_clone = Arc :: clone ( & cache) ;
155
+ let handle = thread:: spawn ( move || {
156
+ let mut rng = thread_rng ( ) ;
157
+
158
+ for _ in 0 ..OPS_PER_THREAD {
159
+ let key = rng. gen_range ( 0 ..1000 ) ;
160
+ if rng. gen :: < bool > ( ) {
161
+ black_box ( cache_clone. insert ( key, key) ) ;
162
+ } else {
163
+ black_box ( cache_clone. get ( & key) ) ;
164
+ }
165
+ }
166
+ } ) ;
167
+ handles. push ( handle) ;
168
+ }
169
+
170
+ for handle in handles {
171
+ handle. join ( ) . unwrap ( ) ;
172
+ }
173
+ } ) ;
174
+ } ) ;
175
+
176
+ group. finish ( ) ;
177
+ }
178
+
74
179
criterion_group ! (
75
180
benches,
76
181
bench_sequence,
77
182
bench_composite,
78
- bench_composite_normal
183
+ bench_composite_normal,
184
+ bench_concurrent_access
79
185
) ;
80
186
criterion_main ! ( benches) ;
0 commit comments