Skip to content

Replace FlatBuffers with serde/postcard for host-guest messages - #2044

Open
ludfjig wants to merge 6 commits into
hyperlight-dev:mainfrom
ludfjig:ll/postcard-only
Open

ludfjig wants to merge 6 commits into
hyperlight-dev:mainfrom
ludfjig:ll/postcard-only

Conversation

@ludfjig

@ludfjig ludfjig commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Implements HIP 0008 (#1999).

Details not covered by the HIP:

  • FunctionCall, ParameterValue and ReturnValue map to private wire types, because their byte payloads travel separately. GuestLogData, GuestEvent and ReturnType derive serde and are encoded directly.
  • abi_tests.rs pins the encoded bytes of every message type. A failure explains that the change breaks snapshots and points to docs/snapshot-versioning.md.
  • Snapshot ABI 10 and goldens v10.0.
  • Floats keep their exact bits, including -0.0.
  • New fuzz target fuzz_control_decode. fuzz_guest_trace_decode replaces the trace size estimator target.
  • Removes estimate_flatbuffer_capacity and the FlatBuffers HyperlightError variants.

Follow-ups: borrow decoded strings, reuse encode buffers (maayybe, need to be careful about security) (#671), remove the chunked flag (#1987), and rename flatbuffer_wrappers.

Fixes #1607

@ludfjig ludfjig added C-refactor For PRs that restructure or remove code without adding new functionality. S-needs-goldens Regenerate snapshot golden fixtures labels Nov 10, 2026
@hyperlight-gh-bot

This comment has been minimized.

@hyperlight-gh-bot

This comment has been minimized.

@hyperlight-gh-bot

This comment has been minimized.

@andreiltd

Copy link
Copy Markdown
Member

Nice! I believe all the: TryFrom<ParameterValue> and  TryFrom<ReturnValue> can be also removed?

@ludfjig
ludfjig marked this pull request as ready for review November 12, 2026 19:45
Copilot AI balanced review requested due to automatic review settings November 12, 2026 19:45
@ludfjig

ludfjig commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor Author

Nice! I believe all the: TryFrom<ParameterValue> and  TryFrom<ReturnValue> can be also removed?

Thanks nice catch. They should go, but i'd prefer do it in a follow up pr, it brings a lot of unrelated code changes if you don't mind and would be a breaking change (they're in public api call_host_function )

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The security documentation overstates allocation guarantees because decoded parameters and trace fields lack per-collection count limits despite transport-size bounds.

1 open issue
What changed in this PR

This PR implements HIP 0008 by using serde/postcard for Hyperlight’s host-guest messages while keeping byte payloads separate from control data.

Changes:

  • Adds postcard wire types and explicit control-data lengths, and removes FlatBuffers schemas, generated code, and dependencies.
  • Updates snapshot compatibility to ABI 10 and goldens v10.0, with exact-bit float assertions.
  • Adds decoder fuzzing and updates communication, security, and development documentation.
File Description
src/​tests/​rust_guests/​Cargo.lock Updates guest serialization dependencies.
src/​schema/​host_function_details.fbs Removes the host-function collection schema.
src/​schema/​host_function_definition.fbs Removes the host-function definition schema.
src/​schema/​guest_trace_data.fbs Removes the trace schema.
src/​schema/​guest_log_data.fbs Removes the log schema.
src/​schema/​guest_error.fbs Removes the guest-error schema.
src/​schema/​function_types.fbs Removes the value and type schemas.
src/​schema/​function_call.fbs Removes the call schema.
src/​schema/​function_call_result.fbs Removes the result schema.
src/​schema/​all.fbs Removes the schema entry point.
src/​hyperlight_host/​tests/​snapshot_goldens/​goldens_version.rs Selects goldens v10.0.
src/​hyperlight_host/​tests/​sandbox_host_tests.rs Checks exact float bits after round trips.
src/​hyperlight_host/​src/​sandbox/​snapshot/​tripwires.rs Pins ABI 10 and references wire-format tests.
src/​hyperlight_host/​src/​sandbox/​snapshot/​file/​media_types.rs Sets snapshot ABI 10.
src/​hyperlight_host/​src/​sandbox/​snapshot/​file_tests.rs Updates snapshot ABI expectations.
src/​hyperlight_host/​src/​sandbox/​outb.rs Passes headers into message decoders.
src/​hyperlight_host/​src/​mem/​virtq/​codec.rs Reads header-sized control data and uses vector encoding.
src/​hyperlight_host/​src/​mem/​shared_mem.rs Updates decoder terminology in safety comments.
src/​hyperlight_host/​src/​mem/​mgr.rs Integrates postcard encoding and header-based decoding.
src/​hyperlight_host/​src/​error.rs Removes FlatBuffers-specific error variants.
src/​hyperlight_host/​Cargo.toml Removes the FlatBuffers dependency.
src/​hyperlight_host/​benches/​benchmarks.rs Adapts codec benchmarks to vector buffers.
src/​hyperlight_guest/​src/​transport/​context.rs Integrates the guest-side encoding and framing changes.
src/​hyperlight_guest/​Cargo.toml Removes the FlatBuffers dependency.
src/​hyperlight_guest_capi/​Cargo.toml Removes the FlatBuffers dependency.
src/​hyperlight_guest_bin/​Cargo.toml Removes the FlatBuffers dependency.
src/​hyperlight_common/​src/​transport.rs Adds control length to the 16-byte header.
src/​hyperlight_common/​src/​lib.rs Removes the generated FlatBuffers module declaration.
src/​hyperlight_common/​src/​flatbuffers/​mod.rs Removes generated module exports.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​return_value_generated.rs Removes generated return-value bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​return_type_generated.rs Removes generated return-type bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​parameter_value_generated.rs Removes generated parameter-value bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​parameter_type_generated.rs Removes generated parameter-type bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​open_span_type_generated.rs Removes generated span-opening bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​log_level_generated.rs Removes generated log-level bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​log_event_type_generated.rs Removes generated log-event bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​key_value_generated.rs Removes generated trace-field bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​host_function_details_generated.rs Removes generated host-function collection bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​host_function_definition_generated.rs Removes generated host-function definition bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​hlvoid_generated.rs Removes generated void bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​hlulong_generated.rs Removes generated unsigned 64-bit bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​hluint_generated.rs Removes generated unsigned 32-bit bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​hlstring_generated.rs Removes generated string bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​hllong_generated.rs Removes generated signed 64-bit bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​hlint_generated.rs Removes generated signed 32-bit bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​hlfloat_generated.rs Removes generated float bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​hlexternalbytes_generated.rs Removes generated external-byte bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​hldouble_generated.rs Removes generated double bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​hlbool_generated.rs Removes generated boolean bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​guest_start_type_generated.rs Removes generated guest-start bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​guest_log_data_generated.rs Removes generated guest-log bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​guest_event_union_type_generated.rs Removes generated event-union bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​guest_event_type_generated.rs Removes generated event-type bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​guest_event_generated.rs Removes generated guest-event bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​guest_error_generated.rs Removes generated guest-error bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​function_call_type_generated.rs Removes generated call-type bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​function_call_result_type_generated.rs Removes generated result-type bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​function_call_result_generated.rs Removes generated call-result bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​function_call_generated.rs Removes generated function-call bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​error_code_generated.rs Removes generated error-code bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​edit_span_type_generated.rs Removes generated span-editing bindings.
src/​hyperlight_common/​src/​flatbuffers/​hyperlight/​generated/​close_span_type_generated.rs Removes generated span-closing bindings.
src/​hyperlight_common/​src/​flatbuffer_wrappers/​wire.rs Defines postcard wire types and encoding helpers.
src/​hyperlight_common/​src/​flatbuffer_wrappers/​mod.rs Registers wire encoding and ABI tests.
src/​hyperlight_common/​src/​flatbuffer_wrappers/​host_function_details.rs Removes FlatBuffers serialization conversions.
src/​hyperlight_common/​src/​flatbuffer_wrappers/​host_function_definition.rs Removes FlatBuffers definition conversions.
src/​hyperlight_common/​src/​flatbuffer_wrappers/​guest_log_level.rs Adds serde support for log levels.
src/​hyperlight_common/​src/​flatbuffer_wrappers/​guest_log_data.rs Uses postcard for logs and adds a round-trip test.
src/​hyperlight_common/​src/​flatbuffer_wrappers/​guest_error.rs Removes generated error-code conversions.
src/​hyperlight_common/​src/​flatbuffer_wrappers/​codec.rs Updates external-value documentation terminology.
src/​hyperlight_common/​Cargo.toml Replaces FlatBuffers with serde and postcard.
README.md Removes the schema-directory listing.
Justfile Updates fuzz targets and removes FlatBuffers tooling recipes.
fuzz/​README.md Documents control-decoder fuzzing.
fuzz/​fuzz_targets/​guest_trace_decode.rs Fuzzes trace decoding and event round trips.
fuzz/​fuzz_targets/​control_decode.rs Fuzzes call, result, and log decoding.
fuzz/​Cargo.toml Registers the decoder fuzz targets.
flake.nix Removes FlatBuffers development tooling.
docs/​virtio-host-guest-communication.md Documents postcard framing and external bytes.
docs/​snapshot-versioning.md Documents ABI 10 and wire-format enforcement.
docs/​security.md Describes serde/postcard decoding at the trust boundary.
docs/​security-guidance-for-developers.md Updates compatibility and message-validation guidance.
docs/​README.md Removes the FlatBuffers guide link.
docs/​how-to-use-flatbuffers.md Removes the obsolete generation guide.
CHANGELOG.md Records the breaking serialization change.
Cargo.lock Updates workspace serialization dependencies.
.github/​workflows/​ValidatePullRequest.yml Runs decoder fuzz targets during PR validation.
.github/​workflows/​Fuzzing.yml Updates the fuzzing target matrix.
.github/​workflows/​DailyArm64.yml Updates the trace-fuzzer name in a comment.
.github/​copilot-instructions.md Removes the obsolete schema-directory listing.

Review effort: Balanced

Comment thread docs/security.md Resolved
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
@hyperlight-gh-bot

This comment has been minimized.

.0
.into_iter()
.map(|value| decode_parameter_value(value, external_values))
.collect::<Result<Vec<ParameterValue>>>()?,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the ParamsIn from Wire already collected the wire values so this does it twice:

https://github.com/hyperlight-dev/hyperlight/pull/2044/changes#diff-7c50d1c378acae3cb80cf61c5c98d7c8a121d40866ba7e525f80fa26e8414b5cR117

This is a memory increase compared to main. I have a reproducer if needed, but it was showing 25-50% increase on large parameters.

I found this with an agent and went through the steps to reproduce it.

@ludfjig ludfjig Nov 13, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, should be fixed in d11f47a, maybe your repro can confirm? Though reason is that previously we allocated vec up front with known encoding size to avoid resizing the vec while filling it. Doing that now too for serde

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having two .collect<>() still causes an extra heap allocation?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only above 8 parameters, since ParamsIn kept 8 inline, but fixed anyway in 0ab3d6c. Now converts parameters as they're decoded into one exactly sized Vec.

Measure the postcard size first so each encode allocates once. A large string followed by more data otherwise doubled the buffer.

Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
@hyperlight-gh-bot

This comment has been minimized.

@andreiltd

Copy link
Copy Markdown
Member

I'm not a big fan of how &MsgHeader is traveling through dispatch loop alongside payload cursor. Could we maybe introduce RecvMessage that is receive side counterpart to EncodedMessage?.

let message = RecvMessage::new(&mut request)?;
let cid = message.cid();

let call = match message.kind() {
    MsgKind::Request => message.decode_call()?,
    // ...
};

and then consuming self into_parts method that internally validates the control length against the remaining input, return allocated storage ans advanced cursor.

let (control, externals) = message.into_parts()?;
let call = FunctionCall::decode(&control, externals)?;

@syntactically syntactically left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems largely sensible to me, although I agree with the other comments (and have mentioned some minor nits as well).

I don't think it needs to be a goal for this PR at all, but I wanted to make sure I was thinking about the position here w.r.t to all the goals that were discussed for the serialisation rework. I remember that @jprendes wanted to make sure that we could layer another runtime-specific Serde-based serialisation layer on top of the core hyperlight layer without adding additional copies/overhead. I think that is still not possible with this? This API still requires realising all the parameters into a list of ByteChunks or VecBytes, and it is not possible to collect all the serialize_bytes calls from a Serde serializable in the way that would be needed---instead we would need a different API where we could make multiple calls to incrementally serialize one parameter at a time, I think?

* `control_len: u32`

`payload_len` covers the control data and all external bytes. RPC correlation
`payload_len` covers the control data and all external bytes. `control_len`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to copy the control data up first?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is what main did, I believe for prevernting UB when guest runs concurrently (I know it doesn't at the moment)

@@ -4,21 +4,14 @@
use alloc::string::{String, ToString};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe these files should not be named flatbuffer_wrappers anymore given that they are not flatbuffers anymore?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, mentioned in the pr description that this will be a follow up for later to keep smaller diff

value: &T,
buffer: &'b mut Vec<u8>,
) -> Result<&'b [u8]> {
let size = postcard::serialize_with_flavor::<T, Size, usize>(value, Size::default())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does having to run the serialization twice here introduce any noticeable performance impact, or is it all inlined/constant-folded to the point it is basically the same as the old code that just did some simple type matches and returned a constant?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made sure the size pass only counts bytes about 4–7 ns

.0
.into_iter()
.map(|value| decode_parameter_value(value, external_values))
.collect::<Result<Vec<ParameterValue>>>()?,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having two .collect<>() still causes an extra heap allocation?

pub host_functions: Option<Vec<HostFunctionDefinition>>,
}

impl TryFrom<&[u8]> for HostFunctionDetails {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These instances were being used in the old module support in hyperlight-wasm. Maybe we can make the types #[derive(Serialize)] so that it is possible to port that with minimal complexity until we get rid of module support?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 03fa0d5. Hyperlight-wasm can use postcard::to_allocvec and postcard::from_bytes on them.

/// [`Bytes`] clone drops.
pub(super) fn decode_request(segments: Segments) -> Result<FunctionCall> {
let (control, mut external_values) = decode_payload(segments)?;
pub(super) fn decode_request(header: &MsgHeader, segments: Segments) -> Result<FunctionCall> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the only thing being used here is the control_len? If so then passing control_len directly instead of MsgHeader is the same size and one fewer memory indirection perhaps? (Not sure if this will get inlined enough for it to not matter, though.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in e6b0c53.

Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
hyperlight-wasm sends HostFunctionDetails to its guest as bytes.

Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
The header travels with its chain instead of being passed to each decode. The guest decodes requests from control_len.

Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
Each parameter is converted as it is read, so decoding builds the final Vec with one allocation.

Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>

@andreiltd andreiltd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great, thank you! 🚀

@hyperlight-gh-bot

Copy link
Copy Markdown

Benchmark Results

Measured commit: 0ab3d6c19382
Baseline commit: 01df99173865

kvm / amd (Linux) (🌟 7.20x)

Top improvements

  • function_call_codec/encode_control/vec_bytes — 🌟 7.20x faster
  • function_call_codec/encode_control/byte_chunks — 🌟 2.71x faster

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 280.83 ns (🌟 2.71x faster)
vec_bytes 82.18 ns (🌟 7.20x faster)
373.19 µs (➖ 1.00x faster)

payload_allocation

slot_pool_segmented
262144 520.94 ns (➖ 1.00x faster)
65536 142.64 ns (➖ 1.03x faster)

sandboxes

create_initialized_and_drop
medium 14.98 ms (➖ 1.03x slower)

slot_pool

alloc_dealloc_128 alloc_dealloc_4096 alloc_dealloc_1500
7.69 ns (➖ 1.06x faster) 7.70 ns (➖ 1.01x faster) 7.69 ns (➖ 1.00x faster)

snapshot_files

load_snapshot_unverified
small 99.77 µs (➖ 1.02x slower)

virtq_readonly

slot_pool_segmented slot_pool_segmented_fragmented
262144 7.08 µs (➖ 1.01x faster) 7.21 µs (➖ 1.12x faster)
65536 1.92 µs (➖ 1.08x faster) 2.02 µs (➖ 1.06x slower)

virtq_readwrite

slot_pool_segmented slot_pool_segmented_fragmented
262144 27.33 µs (➖ 1.00x slower)
65536 6.09 µs (➖ 1.07x faster) 6.32 µs (➖ 1.01x faster)
8192 1.01 µs (➖ 1.00x faster) 1.04 µs (➖ 1.03x slower)
kvm / intel (Linux) (🌟 6.36x)

Top improvements

  • function_call_codec/encode_control/vec_bytes — 🌟 6.36x faster
  • function_call_codec/encode_control/byte_chunks — 🌟 2.60x faster

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 311.47 ns (🌟 2.60x faster)
vec_bytes 98.93 ns (🌟 6.36x faster)
749.15 µs (➖ 1.00x faster)

payload_allocation

slot_pool_segmented
262144 657.86 ns (➖ 1.10x slower)
65536 205.54 ns (➖ 1.00x faster)

sandboxes

create_initialized_and_drop
medium 14.43 ms (➖ 1.01x faster)

slot_pool

alloc_dealloc_128 alloc_dealloc_4096 alloc_dealloc_1500
8.30 ns (➖ 1.02x slower) 8.30 ns (➖ 1.02x slower) 8.29 ns (➖ 1.01x slower)

snapshot_files

load_snapshot_unverified
small 59.26 µs (➖ 1.02x slower)

virtq_readonly

slot_pool_segmented slot_pool_segmented_fragmented
262144 9.12 µs (➖ 1.00x slower) 9.07 µs (➖ 1.01x slower)
65536 2.53 µs (➖ 1.00x slower) 2.53 µs (➖ 1.01x faster)

virtq_readwrite

slot_pool_segmented slot_pool_segmented_fragmented
262144 36.61 µs (➖ 1.01x slower)
65536 8.54 µs (➖ 1.00x slower) 8.50 µs (➖ 1.00x faster)
8192 839.80 ns (➖ 1.04x faster) 862.74 ns (➖ 1.02x faster)
mshv3 / amd (Linux) (🌟 7.04x)

Top improvements

  • function_call_codec/encode_control/vec_bytes — 🌟 7.04x faster
  • function_call_codec/encode_control/byte_chunks — 🌟 2.47x faster

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 377.46 ns (🌟 2.47x faster)
vec_bytes 101.87 ns (🌟 7.04x faster)
337.18 µs (➖ 1.23x slower)

payload_allocation

slot_pool_segmented
262144 731.45 ns (➖ 1.01x faster)
65536 193.59 ns (➖ 1.00x slower)

sandboxes

create_initialized_and_drop
medium 63.44 ms (➖ 1.08x faster)

slot_pool

alloc_dealloc_128 alloc_dealloc_4096 alloc_dealloc_1500
9.70 ns (➖ 1.02x slower) 9.51 ns (➖ 1.04x faster) 9.80 ns (➖ 1.00x slower)

snapshot_files

load_snapshot_unverified
small 107.42 µs (➖ 1.01x slower)

virtq_readonly

slot_pool_segmented slot_pool_segmented_fragmented
262144 9.85 µs (➖ 1.01x slower) 10.83 µs (➖ 1.10x faster)
65536 2.27 µs (➖ 1.01x slower) 2.41 µs (➖ 1.02x faster)

virtq_readwrite

slot_pool_segmented slot_pool_segmented_fragmented
262144 36.49 µs (➖ 1.06x faster)
65536 8.44 µs (➖ 1.16x faster) 8.61 µs (➖ 1.14x faster)
8192 1.20 µs (➖ 1.05x faster) 1.23 µs (➖ 1.02x faster)
mshv3 / intel (Linux) (🌟 7.66x)

Top improvements

  • function_call_codec/encode_control/vec_bytes — 🌟 7.66x faster
  • function_call_codec/encode_control/byte_chunks — 🌟 2.68x faster

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 353.33 ns (🌟 2.68x faster)
vec_bytes 87.54 ns (🌟 7.66x faster)
758.09 µs (➖ 1.05x faster)

payload_allocation

slot_pool_segmented
262144 698.29 ns (➖ 1.04x slower)
65536 230.34 ns (➖ 1.15x faster)

sandboxes

create_initialized_and_drop
medium 87.72 ms (➖ 1.03x faster)

slot_pool

alloc_dealloc_128 alloc_dealloc_4096 alloc_dealloc_1500
8.73 ns (➖ 1.00x faster) 8.73 ns (➖ 1.00x slower) 8.76 ns (➖ 1.00x slower)

snapshot_files

load_snapshot_unverified
small 57.82 µs (➖ 1.03x slower)

virtq_readonly

slot_pool_segmented slot_pool_segmented_fragmented
262144 7.98 µs (➖ 1.03x faster) 7.97 µs (➖ 1.02x faster)
65536 2.31 µs (➖ 1.00x faster) 2.30 µs (➖ 1.01x slower)

virtq_readwrite

slot_pool_segmented slot_pool_segmented_fragmented
262144 38.43 µs (➖ 1.04x faster)
65536 7.48 µs (➖ 1.02x faster) 7.52 µs (➖ 1.01x faster)
8192 908.78 ns (➖ 1.01x faster) 899.19 ns (➖ 1.05x faster)
hyperv-ws2025 / amd (Windows) (🌟 4.89x)

Top improvements

  • function_call_codec/encode_control/vec_bytes — 🌟 4.89x faster
  • function_call_codec/encode_control/byte_chunks — 🌟 2.04x faster

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 563.68 ns (🌟 2.04x faster)
vec_bytes 154.12 ns (🌟 4.89x faster)
2.24 ms (➖ 1.01x slower)

payload_allocation

slot_pool_segmented
262144 863.59 ns (➖ 1.09x slower)
65536 231.77 ns (➖ 1.01x slower)

sandboxes

create_initialized_and_drop
medium 10.79 ms (➖ 1.09x slower)

slot_pool

alloc_dealloc_128 alloc_dealloc_4096 alloc_dealloc_1500
10.58 ns (➖ 1.01x faster) 11.04 ns (➖ 1.01x slower) 10.61 ns (➖ 1.00x faster)

snapshot_files

load_snapshot_unverified
small 654.58 µs (➖ 1.27x faster)

virtq_readonly

slot_pool_segmented slot_pool_segmented_fragmented
262144 8.62 µs (➖ 1.00x slower) 8.59 µs (➖ 1.02x faster)
65536 2.51 µs (➖ 1.00x slower) 2.51 µs (➖ 1.01x faster)

virtq_readwrite

slot_pool_segmented slot_pool_segmented_fragmented
262144 38.92 µs (➖ 1.26x faster)
65536 8.97 µs (➖ 1.00x slower) 8.93 µs (➖ 1.04x faster)
8192 1.38 µs (➖ 1.02x slower) 1.39 µs (➖ 1.04x slower)
hyperv-ws2025 / intel (Windows) (🌟 5.27x)

Top improvements

  • function_call_codec/encode_control/vec_bytes — 🌟 5.27x faster
  • function_call_codec/encode_control/byte_chunks — 🌟 1.99x faster

Benchmark Results

function_call_codec

encode_control decode_vec_bytes_copy
byte_chunks 607.14 ns (🌟 1.99x faster)
vec_bytes 157.26 ns (🌟 5.27x faster)
3.05 ms (➖ 1.00x slower)

payload_allocation

slot_pool_segmented
262144 743.75 ns (➖ 1.04x faster)
65536 212.05 ns (➖ 1.01x faster)

sandboxes

create_initialized_and_drop
medium 14.56 ms (➖ 1.04x slower)

slot_pool

alloc_dealloc_128 alloc_dealloc_4096 alloc_dealloc_1500
9.85 ns (➖ 1.11x faster) 9.92 ns (➖ 1.11x faster) 9.67 ns (➖ 1.02x faster)

snapshot_files

load_snapshot_unverified
small 679.57 µs (➖ 1.00x slower)

virtq_readonly

slot_pool_segmented slot_pool_segmented_fragmented
262144 7.65 µs (➖ 1.00x faster) 7.63 µs (➖ 1.01x faster)
65536 2.27 µs (➖ 1.01x faster) 2.26 µs (➖ 1.03x faster)

virtq_readwrite

slot_pool_segmented slot_pool_segmented_fragmented
262144 51.17 µs (➖ 1.25x slower)
65536 8.52 µs (➖ 1.01x faster) 8.41 µs (➖ 1.00x faster)
8192 1.05 µs (➖ 1.13x faster) 1.06 µs (➖ 1.01x faster)

Reported by cargo ci bench-report --candidate run:43478300586 --config-file bench_report.toml.

@ludfjig

ludfjig commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

I don't think it needs to be a goal for this PR at all, but I wanted to make sure I was thinking about the position here w.r.t to all the goals that were discussed for the serialisation rework. I remember that @jprendes wanted to make sure that we could layer another runtime-specific Serde-based serialisation layer on top of the core hyperlight layer without adding additional copies/overhead. I think that is still not possible with this? This API still requires realising all the parameters into a list of ByteChunks or VecBytes, and it is not possible to collect all the serialize_bytes calls from a Serde serializable in the way that would be needed---instead we would need a different API where we could make multiple calls to incrementally serialize one parameter at a time, I think?

Yeah I haven't considered this at all with this PR. I believe regardless of what pr does, I think it's fine to change in the future, most of this is not public apis

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C-refactor For PRs that restructure or remove code without adding new functionality. S-needs-goldens Regenerate snapshot golden fixtures

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Evaluate serde instead of flatbuffer

5 participants