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.
This commit is contained in:
Richard Bradfield 2020-07-17 09:19:49 +01:00 committed by Tadeo Kondrak
parent 2e330a7e37
commit c74cc25a2f
No known key found for this signature in database
GPG Key ID: D41E092CA43F1D8B
7 changed files with 270 additions and 0 deletions

View File

@ -11,3 +11,12 @@ categories = ["encoding"]
[dependencies] [dependencies]
serde = "1.0" serde = "1.0"
[dev-dependencies]
serde_derive = "1.0"
serde_bytes = "0.11"
criterion = "0.3"
[[bench]]
name = "user_sessions"
harness = false

11
Makefile Normal file
View File

@ -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

8
README
View File

@ -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 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. Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted Unless you explicitly state otherwise, any contribution intentionally submitted

View File

@ -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

View File

@ -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=

View File

@ -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)
}
}

102
benches/user_sessions.rs Normal file
View File

@ -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<u8>,
expires: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct User {
id: u32,
name: String,
email: String,
role: UserRole,
session: Option<Session>,
}
fn admin_sample() -> (User, Vec<u8>) {
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<u8>) {
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::<User>(&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::<User>(&ser).unwrap())
});
group.finish();
}
criterion_group!(admin, serialize_admin, deserialize_admin);
criterion_group!(guest, serialize_guest, deserialize_guest);
criterion_main!(admin, guest);