~tdeo/serde_bare

c74cc25a2fd3b488246f4b8c3f6179d1ddc7a0fe — Richard Bradfield 11 months ago 2e330a7
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.
M Cargo.toml => Cargo.toml +9 -0
@@ 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

A Makefile => Makefile +11 -0
@@ 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

M README => README +8 -0
@@ 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

A benches/go-reference/go.mod => benches/go-reference/go.mod +5 -0
@@ 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

A benches/go-reference/go.sum => benches/go-reference/go.sum +15 -0
@@ 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=

A benches/go-reference/user_test.go => benches/go-reference/user_test.go +120 -0
@@ 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)
	}
}

A benches/user_sessions.rs => benches/user_sessions.rs +102 -0
@@ 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);