@@ -30,6 +30,20 @@ fn create_multiuse_sandbox() -> MultiUseSandbox {
3030 create_uninit_sandbox ( ) . evolve ( ) . unwrap ( )
3131}
3232
33+ fn create_sandbox_with_heap_size ( heap_size_mb : Option < u64 > ) -> MultiUseSandbox {
34+ let path = simple_guest_as_string ( ) . unwrap ( ) ;
35+ let config = if let Some ( size_mb) = heap_size_mb {
36+ let mut config = SandboxConfiguration :: default ( ) ;
37+ config. set_heap_size ( size_mb * 1024 * 1024 ) ; // Convert MB to bytes
38+ Some ( config)
39+ } else {
40+ None
41+ } ;
42+
43+ let uninit_sandbox = UninitializedSandbox :: new ( GuestBinary :: FilePath ( path) , config) . unwrap ( ) ;
44+ uninit_sandbox. evolve ( ) . unwrap ( )
45+ }
46+
3347fn guest_call_benchmark ( c : & mut Criterion ) {
3448 let mut group = c. benchmark_group ( "guest_functions" ) ;
3549
@@ -41,6 +55,29 @@ fn guest_call_benchmark(c: &mut Criterion) {
4155 b. iter ( || sbox. call :: < String > ( "Echo" , "hello\n " . to_string ( ) ) . unwrap ( ) ) ;
4256 } ) ;
4357
58+ // Benchmarks a single guest restore after a guest function call.
59+ // The benchmark only includes the time to reset the sandbox memory after the call.
60+ group. bench_function ( "guest_restore" , |b| {
61+ let mut sbox = create_multiuse_sandbox ( ) ;
62+ let snapshot = sbox. snapshot ( ) . unwrap ( ) ;
63+
64+ b. iter_custom ( |iters| {
65+ let mut total_duration = std:: time:: Duration :: ZERO ;
66+
67+ for _ in 0 ..iters {
68+ // Dirty some pages
69+ sbox. call :: < String > ( "Echo" , "hello\n " . to_string ( ) ) . unwrap ( ) ;
70+
71+ // Measure only the restore operation
72+ let start = std:: time:: Instant :: now ( ) ;
73+ sbox. restore ( & snapshot) . unwrap ( ) ;
74+ total_duration += start. elapsed ( ) ;
75+ }
76+
77+ total_duration
78+ } ) ;
79+ } ) ;
80+
4481 // Benchmarks a single guest function call.
4582 // The benchmark does include the time to reset the sandbox memory after the call.
4683 group. bench_function ( "guest_call_with_restore" , |b| {
@@ -75,37 +112,57 @@ fn guest_call_benchmark(c: &mut Criterion) {
75112 group. finish ( ) ;
76113}
77114
78- fn guest_call_benchmark_large_param ( c : & mut Criterion ) {
79- let mut group = c. benchmark_group ( "guest_functions_with_large_parameters " ) ;
115+ fn guest_call_benchmark_large_params ( c : & mut Criterion ) {
116+ let mut group = c. benchmark_group ( "2_large_parameters " ) ;
80117 #[ cfg( target_os = "windows" ) ]
81118 group. sample_size ( 10 ) ; // This benchmark is very slow on Windows, so we reduce the sample size to avoid long test runs.
82119
83- // This benchmark includes time to first clone a vector and string, so it is not a "pure' benchmark of the guest call, but it's still useful
84- group. bench_function ( "guest_call_with_large_parameters" , |b| {
85- const SIZE : usize = 50 * 1024 * 1024 ; // 50 MB
86- let large_vec = vec ! [ 0u8 ; SIZE ] ;
87- let large_string = unsafe { String :: from_utf8_unchecked ( large_vec. clone ( ) ) } ; // Safety: indeed above vec is valid utf8
120+ // Parameter sizes to test in MB. Each guest call will use two parameters of this size (vec and str).
121+ const PARAM_SIZES_MB : & [ u64 ] = & [ 5 , 20 , 60 ] ;
88122
89- let mut config = SandboxConfiguration :: default ( ) ;
90- config. set_input_data_size ( 2 * SIZE + ( 1024 * 1024 ) ) ; // 2 * SIZE + 1 MB, to allow 1MB for the rest of the serialized function call
91- config. set_heap_size ( SIZE as u64 * 15 ) ;
123+ for & param_size_mb in PARAM_SIZES_MB {
124+ let benchmark_name = format ! ( "guest_call_restore_{}mb_params" , param_size_mb) ;
125+ group. bench_function ( & benchmark_name, |b| {
126+ let param_size_bytes = param_size_mb * 1024 * 1024 ;
92127
93- let sandbox = UninitializedSandbox :: new (
94- GuestBinary :: FilePath ( simple_guest_as_string ( ) . unwrap ( ) ) ,
95- Some ( config) ,
96- )
97- . unwrap ( ) ;
98- let mut sandbox = sandbox. evolve ( ) . unwrap ( ) ;
128+ let large_vec = vec ! [ 0u8 ; param_size_bytes as usize ] ;
129+ let large_string = String :: from_utf8 ( large_vec. clone ( ) ) . unwrap ( ) ;
99130
100- b. iter ( || {
101- sandbox
102- . call_guest_function_by_name :: < ( ) > (
103- "LargeParameters" ,
104- ( large_vec. clone ( ) , large_string. clone ( ) ) ,
105- )
106- . unwrap ( )
131+ let mut config = SandboxConfiguration :: default ( ) ;
132+ config. set_heap_size ( 600 * 1024 * 1024 ) ;
133+ config. set_input_data_size ( 300 * 1024 * 1024 ) ;
134+
135+ let sandbox = UninitializedSandbox :: new (
136+ GuestBinary :: FilePath ( simple_guest_as_string ( ) . unwrap ( ) ) ,
137+ Some ( config) ,
138+ )
139+ . unwrap ( ) ;
140+ let mut sandbox = sandbox. evolve ( ) . unwrap ( ) ;
141+ let snapshot = sandbox. snapshot ( ) . unwrap ( ) ;
142+
143+ // Iter_custom to avoid measure clone time of params
144+ b. iter_custom ( |iters| {
145+ let mut total_duration = std:: time:: Duration :: ZERO ;
146+
147+ for _ in 0 ..iters {
148+ let vec_clone = large_vec. clone ( ) ;
149+ let string_clone = large_string. clone ( ) ;
150+
151+ let start = std:: time:: Instant :: now ( ) ;
152+ sandbox
153+ . call_guest_function_by_name :: < ( ) > (
154+ "LargeParameters" ,
155+ ( vec_clone, string_clone) ,
156+ )
157+ . unwrap ( ) ;
158+ sandbox. restore ( & snapshot) . unwrap ( ) ;
159+ total_duration += start. elapsed ( ) ;
160+ }
161+
162+ total_duration
163+ } ) ;
107164 } ) ;
108- } ) ;
165+ }
109166
110167 group. finish ( ) ;
111168}
@@ -138,9 +195,58 @@ fn sandbox_benchmark(c: &mut Criterion) {
138195 group. finish ( ) ;
139196}
140197
198+ fn sandbox_heap_size_benchmark ( c : & mut Criterion ) {
199+ let mut group = c. benchmark_group ( "sandbox_heap_sizes" ) ;
200+
201+ const HEAP_SIZES_MB : & [ u64 ] = & [ 50 , 500 , 995 ] ;
202+
203+ // Benchmark sandbox creation with default heap size
204+ group. bench_function ( "create_sandbox_default_heap" , |b| {
205+ b. iter_with_large_drop ( || create_sandbox_with_heap_size ( None ) ) ;
206+ } ) ;
207+
208+ // Benchmark sandbox creation with different heap sizes
209+ for & heap_size_mb in HEAP_SIZES_MB {
210+ let benchmark_name = format ! ( "create_sandbox_{}mb_heap" , heap_size_mb) ;
211+ group. bench_function ( & benchmark_name, |b| {
212+ b. iter_with_large_drop ( || create_sandbox_with_heap_size ( Some ( heap_size_mb) ) ) ;
213+ } ) ;
214+ }
215+
216+ group. finish ( ) ;
217+ }
218+
219+ fn guest_call_heap_size_benchmark ( c : & mut Criterion ) {
220+ let mut group = c. benchmark_group ( "guest_call_restore_heap_sizes" ) ;
221+
222+ const HEAP_SIZES_MB : & [ Option < u64 > ] =
223+ & [ None , Some ( 50 ) , Some ( 100 ) , Some ( 250 ) , Some ( 500 ) , Some ( 995 ) ] ;
224+
225+ // Benchmark guest function call with different heap sizes (including default)
226+ for & heap_size_mb in HEAP_SIZES_MB {
227+ let benchmark_name = match heap_size_mb {
228+ None => "guest_call_restore_default_mb_heap" . to_string ( ) ,
229+ Some ( size) => format ! ( "guest_call_restore_{}_mb_heap" , size) ,
230+ } ;
231+ group. bench_function ( & benchmark_name, |b| {
232+ let mut sandbox = create_sandbox_with_heap_size ( heap_size_mb) ;
233+ let snapshot = sandbox. snapshot ( ) . unwrap ( ) ;
234+
235+ b. iter ( || {
236+ sandbox
237+ . call_guest_function_by_name :: < String > ( "Echo" , "hello\n " . to_string ( ) )
238+ . unwrap ( ) ;
239+ sandbox. restore ( & snapshot) . unwrap ( ) ;
240+ } ) ;
241+ } ) ;
242+ }
243+
244+ group. finish ( ) ;
245+ }
246+
141247criterion_group ! {
142248 name = benches;
143249 config = Criterion :: default ( ) ;
144- targets = guest_call_benchmark, sandbox_benchmark, guest_call_benchmark_large_param
250+ targets = guest_call_benchmark, sandbox_benchmark, sandbox_heap_size_benchmark , guest_call_benchmark_large_params , guest_call_heap_size_benchmark
145251}
146252criterion_main ! ( benches) ;
0 commit comments