From c74cc25a2fd3b488246f4b8c3f6179d1ddc7a0fe Mon Sep 17 00:00:00 2001 From: Richard Bradfield Date: Fri, 17 Jul 2020 09:19:49 +0100 Subject: [PATCH] Add Criterion benchmarks Introduces a (hopefully) representative set of benchmarks for an imaginary 'User session token', with a variable size depending on which fields are populated. Does not yet cover floating point or union types. Also included is a go-bare implementation of an identical benchmark, so that we can compare our implementation again the 'reference' code. --- Cargo.toml | 9 +++ Makefile | 11 +++ README | 8 ++ benches/go-reference/go.mod | 5 ++ benches/go-reference/go.sum | 15 ++++ benches/go-reference/user_test.go | 120 ++++++++++++++++++++++++++++++ benches/user_sessions.rs | 102 +++++++++++++++++++++++++ 7 files changed, 270 insertions(+) create mode 100644 Makefile create mode 100644 benches/go-reference/go.mod create mode 100644 benches/go-reference/go.sum create mode 100644 benches/go-reference/user_test.go create mode 100644 benches/user_sessions.rs diff --git a/Cargo.toml b/Cargo.toml index e30fbc8..1058dc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,12 @@ categories = ["encoding"] [dependencies] serde = "1.0" + +[dev-dependencies] +serde_derive = "1.0" +serde_bytes = "0.11" +criterion = "0.3" + +[[bench]] +name = "user_sessions" +harness = false diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..79a2573 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +# Provided so users not familiar with Go don't need to know the go bench invocation + +.PHONY: bench go-bench + +bench: + cargo bench + +go-bench: + cd benches/go-reference && go test -bench=. + +bench-all: bench go-bench diff --git a/README b/README index be41b8f..5f39ca9 100644 --- a/README +++ b/README @@ -4,6 +4,14 @@ An implementation of the BARE (https://git.sr.ht/~sircmpwn/bare) encoding format Mailing list: https://lists.sr.ht/~tdeo/serde_bare +To run benchmarks on your system: + + make bench + +Or to run the reference Go benchmarks as well: + + make bench-all + Licensed under either of Apache License, Version 2.0 or MIT license at your option. Unless you explicitly state otherwise, any contribution intentionally submitted diff --git a/benches/go-reference/go.mod b/benches/go-reference/go.mod new file mode 100644 index 0000000..a59b80c --- /dev/null +++ b/benches/go-reference/go.mod @@ -0,0 +1,5 @@ +module git.sr.ht/~tdeo/serde_bare/benches/go-reference + +go 1.14 + +require git.sr.ht/~sircmpwn/go-bare v0.0.0-20200623145341-debb068b456a diff --git a/benches/go-reference/go.sum b/benches/go-reference/go.sum new file mode 100644 index 0000000..94332ef --- /dev/null +++ b/benches/go-reference/go.sum @@ -0,0 +1,15 @@ +git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw= +git.sr.ht/~sircmpwn/go-bare v0.0.0-20200623145341-debb068b456a h1:bqT/ygbVtD6b/B/skCQ+hZI4OtwuyKFQJxwS3z1lY3g= +git.sr.ht/~sircmpwn/go-bare v0.0.0-20200623145341-debb068b456a/go.mod h1:BVJwbDfVjCjoFiKrhkei6NdGcZYpkDkdyCdg1ukytRA= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/benches/go-reference/user_test.go b/benches/go-reference/user_test.go new file mode 100644 index 0000000..e0d9e0c --- /dev/null +++ b/benches/go-reference/user_test.go @@ -0,0 +1,120 @@ +package main + +import ( + "bytes" + "git.sr.ht/~sircmpwn/go-bare" + "log" + "testing" +) + +type UserRole uint + +const ( + Admin UserRole = 0 + Normal = 1 + Guest = 2 +) + +type Session struct { + Token []byte + Expires uint +} + +type User struct { + ID uint + Name string + Email string + Role UserRole + Session *Session +} + +func makeAdmin() ([]byte, User) { + session := Session{ + Token: []byte("a2b08ecd0a0dc594ebccd607033e79262d1fa049a6d44165631b10028f97b611"), + Expires: 42424242, + } + + admin := User{ + ID: 42, + Name: "Jane Doe", + Email: "jdoe@example.com", + Role: Admin, + Session: &session, + } + + marshalled, err := bare.Marshal(&admin) + + if err != nil { + log.Fatalf("Failed to marshal: %s", err) + } + + return marshalled, admin +} + +func makeGuest() ([]byte, User) { + guest := User{ + ID: 112, + Name: "John Smith", + Email: "john@example.com", + Role: Guest, + Session: nil, + } + + marshalled, err := bare.Marshal(&guest) + + if err != nil { + log.Fatalf("Failed to marshal: %s", err) + } + + return marshalled, guest +} + +func BenchmarkAdminSerialize(b *testing.B) { + s, admin := makeAdmin() + var buf bytes.Buffer + buf.Grow(128) + + b.ResetTimer() + b.SetBytes(int64(len(s))) + for n := 0; n < b.N; n++ { + w := bare.NewWriter(&buf) + bare.MarshalWriter(w, admin) + buf.Reset() + } +} + +func BenchmarkAdminDeserialize(b *testing.B) { + s, _ := makeAdmin() + var output User + + b.ResetTimer() + b.SetBytes(int64(len(s))) + for n := 0; n < b.N; n++ { + _ = bare.Unmarshal(s, &output) + } +} + +func BenchmarkGuestSerialize(b *testing.B) { + s, guest := makeGuest() + var buf bytes.Buffer + buf.Grow(128) + + b.ResetTimer() + b.SetBytes(int64(len(s))) + for n := 0; n < b.N; n++ { + w := bare.NewWriter(&buf) + bare.MarshalWriter(w, guest) + buf.Reset() + } +} + +func BenchmarkGuestDeserialize(b *testing.B) { + s, _ := makeGuest() + var output User + + b.ResetTimer() + b.SetBytes(int64(len(s))) + for n := 0; n < b.N; n++ { + _ = bare.Unmarshal(s, &output) + } +} diff --git a/benches/user_sessions.rs b/benches/user_sessions.rs new file mode 100644 index 0000000..2436f03 --- /dev/null +++ b/benches/user_sessions.rs @@ -0,0 +1,102 @@ +use criterion::{criterion_group, criterion_main, Criterion, Throughput}; +use serde_derive::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +enum UserRole { + Admin, + User, + Guest, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct Session { + #[serde(with = "serde_bytes")] + token: Vec, + expires: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct User { + id: u32, + name: String, + email: String, + role: UserRole, + session: Option, +} + +fn admin_sample() -> (User, Vec) { + let sample = User { + id: 42, + name: "Jane Doe".to_string(), + email: "jdoe@example.com".to_string(), + role: UserRole::Admin, + session: Some(Session { + token: b"a2b08ecd0a0dc594ebccd607033e79262d1fa049a6d44165631b10028f97b611".to_vec(), + expires: 42424242, + }), + }; + let ser = serde_bare::to_vec(&sample).unwrap(); + (sample, ser) +} + +fn guest_sample() -> (User, Vec) { + let sample = User { + id: 112, + name: "John Doe".to_string(), + email: "john@example.com".to_string(), + role: UserRole::Guest, + session: None, + }; + let ser = serde_bare::to_vec(&sample).unwrap(); + (sample, ser) +} + +fn serialize_admin(c: &mut Criterion) { + let (sample, ser) = admin_sample(); + let mut group = c.benchmark_group("serialization"); + group.throughput(Throughput::Bytes(ser.len() as u64)); + + let mut buffer: [u8; 128] = [0; 128]; + group.bench_function("serialize admin", |b| { + b.iter(|| serde_bare::to_writer(&mut buffer[..], &sample).unwrap()) + }); + group.finish(); +} + +fn deserialize_admin(c: &mut Criterion) { + let (_, ser) = admin_sample(); + let mut group = c.benchmark_group("deserialization"); + group.throughput(Throughput::Bytes(ser.len() as u64)); + + group.bench_function("deserialize admin", |b| { + b.iter(|| serde_bare::from_slice::(&ser).unwrap()) + }); + group.finish(); +} + +fn serialize_guest(c: &mut Criterion) { + let (sample, ser) = guest_sample(); + let mut group = c.benchmark_group("serialization"); + group.throughput(Throughput::Bytes(ser.len() as u64)); + + let mut buffer: [u8; 128] = [0; 128]; + group.bench_function("serialize guest", |b| { + b.iter(|| serde_bare::to_writer(&mut buffer[..], &sample).unwrap()) + }); + group.finish(); +} + +fn deserialize_guest(c: &mut Criterion) { + let (_, ser) = guest_sample(); + let mut group = c.benchmark_group("deserialization"); + group.throughput(Throughput::Bytes(ser.len() as u64)); + + group.bench_function("deserialize guest", |b| { + b.iter(|| serde_bare::from_slice::(&ser).unwrap()) + }); + group.finish(); +} + +criterion_group!(admin, serialize_admin, deserialize_admin); +criterion_group!(guest, serialize_guest, deserialize_guest); +criterion_main!(admin, guest);