Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(quick-protobuf-codec): reduce allocations during encoding #4782

Merged
merged 22 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c4b3341
Add benchmark
thomaseizinger Nov 9, 2023
ef00f01
Add test for encoding large message
thomaseizinger Nov 9, 2023
8f3df39
Don't allocate as much
thomaseizinger Nov 2, 2023
58a27fb
Add changelog entry
thomaseizinger Nov 2, 2023
466d049
Add test for max length
thomaseizinger Nov 2, 2023
5b79d2d
Ensure we reserve enough space for new message
thomaseizinger Nov 2, 2023
a43c1de
Update misc/quick-protobuf-codec/CHANGELOG.md
thomaseizinger Nov 2, 2023
9959d16
Ensure we have enough capacity in `BytesMut`
thomaseizinger Nov 2, 2023
5e49ff1
Don't allocate that many bytes
thomaseizinger Nov 2, 2023
cb90431
Don't test codec in gossipsub
thomaseizinger Nov 2, 2023
51bbb90
Add test to codec module
thomaseizinger Nov 2, 2023
30d57ab
Move utilities to the bottom
thomaseizinger Nov 2, 2023
544130e
Improve naming
thomaseizinger Nov 2, 2023
fb10b17
Split functions to improve readability around unsafe code
thomaseizinger Nov 2, 2023
36bc963
Add more unit tests
thomaseizinger Nov 2, 2023
6cf3642
Refactor `decode` for clarity
thomaseizinger Nov 2, 2023
6eb2e55
Fix compile error
thomaseizinger Nov 2, 2023
7893b81
Write slice in single command
thomaseizinger Nov 9, 2023
47dd867
Merge branch 'master' into deps/less-allocations-in-codec
thomaseizinger Nov 19, 2023
9e19b84
Refactor to use `BytesMut` APIs
thomaseizinger Nov 21, 2023
5514f2e
Update misc/quick-protobuf-codec/CHANGELOG.md
thomaseizinger Nov 21, 2023
110433d
Merge branch 'master' into deps/less-allocations-in-codec
mergify[bot] Nov 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ multiaddr = "0.18.1"
multihash = "0.19.1"
multistream-select = { version = "0.13.0", path = "misc/multistream-select" }
prometheus-client = "0.22.0"
quick-protobuf-codec = { version = "0.3.0", path = "misc/quick-protobuf-codec" }
quick-protobuf-codec = { version = "0.3.1", path = "misc/quick-protobuf-codec" }
quickcheck = { package = "quickcheck-ext", path = "misc/quickcheck-ext" }
rw-stream-sink = { version = "0.4.0", path = "misc/rw-stream-sink" }
unsigned-varint = { version = "0.8.0" }
Expand Down
5 changes: 5 additions & 0 deletions misc/quick-protobuf-codec/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.3.1

- Reduce allocations during encoding.
See [PR 4782](https://github.com/libp2p/rust-libp2p/pull/4782).

## 0.3.0

- Update to `asynchronous-codec` `v0.7.0`.
Expand Down
13 changes: 11 additions & 2 deletions misc/quick-protobuf-codec/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "quick-protobuf-codec"
edition = "2021"
rust-version = { workspace = true }
description = "Asynchronous de-/encoding of Protobuf structs using asynchronous-codec, unsigned-varint and quick-protobuf."
version = "0.3.0"
version = "0.3.1"
authors = ["Max Inden <[email protected]>"]
license = "MIT"
repository = "https://github.com/libp2p/rust-libp2p"
Expand All @@ -14,9 +14,18 @@ categories = ["asynchronous"]
asynchronous-codec = { workspace = true }
bytes = { version = "1" }
thiserror = "1.0"
unsigned-varint = { workspace = true, features = ["asynchronous_codec"] }
unsigned-varint = { workspace = true, features = ["std"] }
quick-protobuf = "0.8"

[dev-dependencies]
criterion = "0.5.1"
futures = "0.3.28"
quickcheck = { workspace = true }

[[bench]]
name = "codec"
harness = false

# Passing arguments to the docsrs builder in order to properly document cfg's.
# More information: https://docs.rs/about/builds#cross-compiling
[package.metadata.docs.rs]
Expand Down
28 changes: 28 additions & 0 deletions misc/quick-protobuf-codec/benches/codec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use asynchronous_codec::Encoder;
use bytes::BytesMut;
use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};
use quick_protobuf_codec::{proto, Codec};

pub fn benchmark(c: &mut Criterion) {
for size in [1000, 10_000, 100_000, 1_000_000, 10_000_000] {
c.bench_with_input(BenchmarkId::new("encode", size), &size, |b, i| {
b.iter_batched(
|| {
let mut out = BytesMut::new();
out.reserve(i + 100);
let codec = Codec::<proto::Message>::new(i + 100);
let msg = proto::Message {
data: vec![0; size],
};

(codec, out, msg)
},
|(mut codec, mut out, msg)| codec.encode(msg, &mut out).unwrap(),
BatchSize::SmallInput,
);
});
}
}

criterion_group!(benches, benchmark);
criterion_main!(benches);
2 changes: 2 additions & 0 deletions misc/quick-protobuf-codec/src/generated/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Automatically generated mod.rs
pub mod test;
7 changes: 7 additions & 0 deletions misc/quick-protobuf-codec/src/generated/test.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
syntax = "proto3";

package test;

message Message {
bytes data = 1;
}
47 changes: 47 additions & 0 deletions misc/quick-protobuf-codec/src/generated/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Automatically generated rust module for 'test.proto' file

#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(unused_imports)]
#![allow(unknown_lints)]
#![allow(clippy::all)]
#![cfg_attr(rustfmt, rustfmt_skip)]


use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result};
use quick_protobuf::sizeofs::*;
use super::*;

#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Debug, Default, PartialEq, Clone)]
pub struct Message {
pub data: Vec<u8>,
}

impl<'a> MessageRead<'a> for Message {
fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result<Self> {
let mut msg = Self::default();
while !r.is_eof() {
match r.next_tag(bytes) {
Ok(10) => msg.data = r.read_bytes(bytes)?.to_owned(),
Ok(t) => { r.read_unknown(bytes, t)?; }
Err(e) => return Err(e),
}
}
Ok(msg)
}
}

impl MessageWrite for Message {
fn get_size(&self) -> usize {
0
+ if self.data.is_empty() { 0 } else { 1 + sizeof_len((&self.data).len()) }
}

fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
if !self.data.is_empty() { w.write_with_tag(10, |w| w.write_bytes(&**&self.data))?; }
Ok(())
}
}

Loading