Skip to content

Commit 0c83035

Browse files
committed
Make serializing a FunctionCall return a &[u8] instead of Vec<u8>.
This should save memory allocations, but require that a FlatBufferBuilder is passed in. Signed-off-by: Ludvig Liljenberg <[email protected]>
1 parent 253af5c commit 0c83035

File tree

12 files changed

+159
-167
lines changed

12 files changed

+159
-167
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/hyperlight_common/src/flatbuffer_wrappers/function_call.rs

Lines changed: 116 additions & 144 deletions
Large diffs are not rendered by default.

src/hyperlight_guest/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ anyhow = { version = "1.0.99", default-features = false }
1616
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
1717
hyperlight-common = { workspace = true }
1818
hyperlight-guest-tracing = { workspace = true, default-features = false }
19+
flatbuffers = { version= "25.2.10", default-features = false }
1920

2021
[features]
2122
default = []

src/hyperlight_guest/src/guest_handle/host_comm.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use alloc::string::ToString;
1919
use alloc::vec::Vec;
2020
use core::slice::from_raw_parts;
2121

22+
use flatbuffers::FlatBufferBuilder;
2223
use hyperlight_common::flatbuffer_wrappers::function_call::{FunctionCall, FunctionCallType};
2324
use hyperlight_common::flatbuffer_wrappers::function_types::{
2425
ParameterValue, ReturnType, ReturnValue,
@@ -27,6 +28,7 @@ use hyperlight_common::flatbuffer_wrappers::guest_error::{ErrorCode, GuestError}
2728
use hyperlight_common::flatbuffer_wrappers::guest_log_data::GuestLogData;
2829
use hyperlight_common::flatbuffer_wrappers::guest_log_level::LogLevel;
2930
use hyperlight_common::flatbuffer_wrappers::host_function_details::HostFunctionDetails;
31+
use hyperlight_common::flatbuffer_wrappers::util::estimate_flatbuffer_capacity;
3032
use hyperlight_common::outb::OutBAction;
3133

3234
use super::handle::GuestHandle;
@@ -92,17 +94,19 @@ impl GuestHandle {
9294
parameters: Option<Vec<ParameterValue>>,
9395
return_type: ReturnType,
9496
) -> Result<()> {
97+
let estimated_capacity =
98+
estimate_flatbuffer_capacity(function_name, parameters.as_deref().unwrap_or(&[]));
99+
95100
let host_function_call = FunctionCall::new(
96101
function_name.to_string(),
97102
parameters,
98103
FunctionCallType::Host,
99104
return_type,
100105
);
101106

102-
let host_function_call_buffer: Vec<u8> = host_function_call
103-
.try_into()
104-
.expect("Unable to serialize host function call");
107+
let mut builder = FlatBufferBuilder::with_capacity(estimated_capacity);
105108

109+
let host_function_call_buffer = host_function_call.encode(&mut builder);
106110
self.push_shared_output_data(host_function_call_buffer)?;
107111

108112
unsafe {
@@ -155,7 +159,7 @@ impl GuestHandle {
155159
.try_into()
156160
.expect("Invalid guest_error_buffer, could not be converted to a Vec<u8>");
157161

158-
if let Err(e) = self.push_shared_output_data(guest_error_buffer) {
162+
if let Err(e) = self.push_shared_output_data(&guest_error_buffer) {
159163
panic!("Unable to push guest error to shared output data: {:#?}", e);
160164
}
161165
}
@@ -184,7 +188,7 @@ impl GuestHandle {
184188
.try_into()
185189
.expect("Failed to convert GuestLogData to bytes");
186190

187-
self.push_shared_output_data(bytes)
191+
self.push_shared_output_data(&bytes)
188192
.expect("Unable to push log data to shared output data");
189193

190194
unsafe {

src/hyperlight_guest/src/guest_handle/io.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ limitations under the License.
1616

1717
use alloc::format;
1818
use alloc::string::ToString;
19-
use alloc::vec::Vec;
2019
use core::any::type_name;
2120
use core::slice::from_raw_parts_mut;
2221

@@ -93,7 +92,7 @@ impl GuestHandle {
9392

9493
/// Pushes the given data onto the shared output data buffer.
9594
#[hyperlight_guest_tracing::trace_function]
96-
pub fn push_shared_output_data(&self, data: Vec<u8>) -> Result<()> {
95+
pub fn push_shared_output_data(&self, data: &[u8]) -> Result<()> {
9796
let peb_ptr = self.peb().unwrap();
9897
let output_stack_size = unsafe { (*peb_ptr).output_stack.size as usize };
9998
let output_stack_ptr = unsafe { (*peb_ptr).output_stack.ptr as *mut u8 };
@@ -139,7 +138,7 @@ impl GuestHandle {
139138

140139
// write the actual data
141140
hyperlight_guest_tracing::trace!("copy data", {
142-
odb[stack_ptr_rel as usize..stack_ptr_rel as usize + data.len()].copy_from_slice(&data);
141+
odb[stack_ptr_rel as usize..stack_ptr_rel as usize + data.len()].copy_from_slice(data);
143142
});
144143

145144
// write the offset to the newly written data, to the top of the stack

src/hyperlight_guest_bin/src/guest_function/call.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ fn internal_dispatch_function() -> Result<()> {
9898
handle.write_error(e.kind, Some(e.message.as_str()));
9999
})?;
100100

101-
handle.push_shared_output_data(result_vec)
101+
handle.push_shared_output_data(&result_vec)
102102
}
103103

104104
// This is implemented as a separate function to make sure that epilogue in the internal_dispatch_function is called before the halt()

src/hyperlight_host/benches/benchmarks.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ limitations under the License.
1515
*/
1616

1717
use criterion::{Criterion, criterion_group, criterion_main};
18+
use flatbuffers::FlatBufferBuilder;
1819
use hyperlight_common::flatbuffer_wrappers::function_call::{FunctionCall, FunctionCallType};
1920
use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, ReturnType};
21+
use hyperlight_common::flatbuffer_wrappers::util::estimate_flatbuffer_capacity;
2022
use hyperlight_host::GuestBinary;
2123
use hyperlight_host::sandbox::{MultiUseSandbox, SandboxConfiguration, UninitializedSandbox};
2224
use hyperlight_testing::{c_simple_guest_as_string, simple_guest_as_string};
@@ -157,20 +159,26 @@ fn function_call_serialization_benchmark(c: &mut Criterion) {
157159
ReturnType::Int,
158160
);
159161

160-
let serialized_bytes: Vec<u8> = function_call.clone().try_into().unwrap();
161-
162162
group.bench_function("serialize_function_call", |b| {
163-
b.iter_with_setup(
164-
|| function_call.clone(),
165-
|fc| {
166-
let _serialized: Vec<u8> = fc.try_into().unwrap();
167-
},
168-
);
163+
b.iter(|| {
164+
// We specifically want to include the time to estimate the capacity in this benchmark
165+
let estimated_capacity = estimate_flatbuffer_capacity(
166+
function_call.function_name.as_str(),
167+
function_call.parameters.as_deref().unwrap_or(&[]),
168+
);
169+
let mut builder = FlatBufferBuilder::with_capacity(estimated_capacity);
170+
let serialized: &[u8] = function_call.encode(&mut builder);
171+
std::hint::black_box(serialized);
172+
});
169173
});
170174

171175
group.bench_function("deserialize_function_call", |b| {
176+
let mut builder = FlatBufferBuilder::new();
177+
let bytes = function_call.clone().encode(&mut builder);
178+
172179
b.iter(|| {
173-
let _deserialized: FunctionCall = serialized_bytes.as_slice().try_into().unwrap();
180+
let deserialized: FunctionCall = bytes.try_into().unwrap();
181+
std::hint::black_box(deserialized);
174182
});
175183
});
176184

src/hyperlight_host/src/sandbox/initialized_multi_use.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ use std::path::Path;
2323
use std::sync::atomic::{AtomicU64, Ordering};
2424
use std::sync::{Arc, Mutex};
2525

26+
use flatbuffers::FlatBufferBuilder;
2627
use hyperlight_common::flatbuffer_wrappers::function_call::{FunctionCall, FunctionCallType};
2728
use hyperlight_common::flatbuffer_wrappers::function_types::{
2829
ParameterValue, ReturnType, ReturnValue,
2930
};
31+
use hyperlight_common::flatbuffer_wrappers::util::estimate_flatbuffer_capacity;
3032
use tracing::{Span, instrument};
3133

3234
use super::host_funcs::FunctionRegistry;
@@ -44,7 +46,7 @@ use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
4446
use crate::mem::ptr::RawPtr;
4547
use crate::mem::shared_mem::HostSharedMemory;
4648
use crate::metrics::maybe_time_and_emit_guest_call;
47-
use crate::{HyperlightError, Result, log_then_return};
49+
use crate::{Result, log_then_return};
4850

4951
/// Global counter for assigning unique IDs to sandboxes
5052
static SANDBOX_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
@@ -392,20 +394,21 @@ impl MultiUseSandbox {
392394
args: Vec<ParameterValue>,
393395
) -> Result<ReturnValue> {
394396
let res = (|| {
397+
let estimated_capacity = estimate_flatbuffer_capacity(function_name, &args);
398+
395399
let fc = FunctionCall::new(
396400
function_name.to_string(),
397401
Some(args),
398402
FunctionCallType::Guest,
399403
return_type,
400404
);
401405

402-
let buffer: Vec<u8> = fc.try_into().map_err(|_| {
403-
HyperlightError::Error("Failed to serialize FunctionCall".to_string())
404-
})?;
406+
let mut builder = FlatBufferBuilder::with_capacity(estimated_capacity);
407+
let buffer = fc.encode(&mut builder);
405408

406409
self.get_mgr_wrapper_mut()
407410
.as_mut()
408-
.write_guest_function_call(&buffer)?;
411+
.write_guest_function_call(buffer)?;
409412

410413
self.vm.dispatch_call_from_host(
411414
self.dispatch_ptr.clone(),

src/tests/rust_guests/callbackguest/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/tests/rust_guests/dummyguest/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)