296 lines
8.3 KiB
Rust
296 lines
8.3 KiB
Rust
// Copyright 2019 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use bencher::Bencher;
|
|
use flexbuffers::*;
|
|
|
|
fn push_vec_u64_to_map(b: &mut Bencher) {
|
|
let va = vec![u64::max_value() - 10; 512];
|
|
let vb = vec![u64::max_value() - 20; 512];
|
|
let vc = vec![u64::max_value() - 30; 512];
|
|
let mut n = 0;
|
|
|
|
b.iter(|| {
|
|
let mut fxb = Builder::default();
|
|
let mut m = fxb.start_map();
|
|
let mut ma = m.start_vector("a");
|
|
for &a in va.iter() {
|
|
ma.push(a);
|
|
}
|
|
ma.end_vector();
|
|
let mut mb = m.start_vector("b");
|
|
for &b in vb.iter() {
|
|
mb.push(b);
|
|
}
|
|
mb.end_vector();
|
|
let mut mc = m.start_vector("c");
|
|
for &c in vc.iter() {
|
|
mc.push(c);
|
|
}
|
|
mc.end_vector();
|
|
m.end_map();
|
|
n = fxb.view().len();
|
|
});
|
|
b.bytes = n as u64;
|
|
}
|
|
fn push_vec_u64_to_map_reused(b: &mut Bencher) {
|
|
let va = vec![u64::max_value() - 10; 512];
|
|
let vb = vec![u64::max_value() - 20; 512];
|
|
let vc = vec![u64::max_value() - 30; 512];
|
|
let mut fxb = Builder::default();
|
|
let mut n = 0;
|
|
let mut go = || {
|
|
let mut m = fxb.start_map();
|
|
let mut ma = m.start_vector("a");
|
|
for &a in va.iter() {
|
|
ma.push(a);
|
|
}
|
|
ma.end_vector();
|
|
let mut mb = m.start_vector("b");
|
|
for &b in vb.iter() {
|
|
mb.push(b);
|
|
}
|
|
mb.end_vector();
|
|
let mut mc = m.start_vector("c");
|
|
for &c in vc.iter() {
|
|
mc.push(c);
|
|
}
|
|
mc.end_vector();
|
|
m.end_map();
|
|
n = fxb.view().len();
|
|
};
|
|
go(); // warm up allocations.
|
|
b.iter(go);
|
|
b.bytes = n as u64;
|
|
}
|
|
fn push_vec_u64_to_map_direct(b: &mut Bencher) {
|
|
let va = vec![u64::max_value() - 10; 512];
|
|
let vb = vec![u64::max_value() - 20; 512];
|
|
let vc = vec![u64::max_value() - 30; 512];
|
|
let mut n = 0;
|
|
|
|
b.iter(|| {
|
|
let mut fxb = Builder::default();
|
|
let mut m = fxb.start_map();
|
|
m.push("a", &va);
|
|
m.push("b", &vb);
|
|
m.push("c", &vc);
|
|
m.end_map();
|
|
n = fxb.view().len();
|
|
});
|
|
b.bytes = n as u64;
|
|
}
|
|
fn push_vec_u64_to_map_direct_reused(b: &mut Bencher) {
|
|
let va = vec![u64::max_value() - 10; 512];
|
|
let vb = vec![u64::max_value() - 20; 512];
|
|
let vc = vec![u64::max_value() - 30; 512];
|
|
let mut n = 0;
|
|
let mut fxb = Builder::default();
|
|
let mut go = || {
|
|
let mut m = fxb.start_map();
|
|
m.push("a", &va);
|
|
m.push("b", &vb);
|
|
m.push("c", &vc);
|
|
m.end_map();
|
|
n = fxb.view().len();
|
|
};
|
|
go(); // warm up allocations.
|
|
b.iter(go);
|
|
b.bytes = n as u64;
|
|
}
|
|
|
|
fn push_vec_without_indirect(b: &mut Bencher) {
|
|
let mut builder = Builder::default();
|
|
let mut n = 0;
|
|
let mut go = || {
|
|
let mut b = builder.start_vector();
|
|
for i in 0..1024u16 {
|
|
b.push(i);
|
|
}
|
|
b.push(i64::max_value());
|
|
b.end_vector();
|
|
n = builder.view().len();
|
|
};
|
|
go(); // warm up allocations.
|
|
b.iter(go);
|
|
b.bytes = n as u64;
|
|
}
|
|
// This isn't actually faster than the alternative but it is a lot smaller.
|
|
// Based on the above benchmarks a lot of time is stuck in the `values` stack.
|
|
fn push_vec_with_indirect(b: &mut Bencher) {
|
|
let mut builder = Builder::default();
|
|
let mut n = 0;
|
|
let mut go = || {
|
|
let mut b = builder.start_vector();
|
|
for i in 0..1024u16 {
|
|
b.push(i);
|
|
}
|
|
b.push(IndirectInt(i64::max_value()));
|
|
b.end_vector();
|
|
n = builder.view().len();
|
|
};
|
|
go(); // warm up allocations.
|
|
b.iter(go);
|
|
b.bytes = n as u64;
|
|
}
|
|
|
|
fn example_map<'a>(m: &mut MapBuilder<'a>) {
|
|
m.push("some_ints", &[256; 5]);
|
|
m.push("some_uints", &[256u16; 5]);
|
|
m.push("some_floats", &[256f32; 5]);
|
|
m.push("some_strings", "muahahahahaha");
|
|
}
|
|
fn hundred_maps(b: &mut Bencher) {
|
|
let mut builder = Builder::default();
|
|
let mut n = 0;
|
|
let mut go = || {
|
|
let mut v = builder.start_vector();
|
|
for _ in 0..100 {
|
|
example_map(&mut v.start_map());
|
|
}
|
|
v.end_vector();
|
|
n = builder.view().len();
|
|
};
|
|
go(); // Warm up allocations.
|
|
b.iter(go);
|
|
b.bytes = n as u64;
|
|
}
|
|
fn hundred_maps_pooled(b: &mut Bencher) {
|
|
let mut builder = Builder::default();
|
|
let mut n = 0;
|
|
let mut go = || {
|
|
let mut v = builder.start_vector();
|
|
for _ in 0..100 {
|
|
example_map(&mut v.start_map());
|
|
}
|
|
v.end_vector();
|
|
n = builder.view().len();
|
|
};
|
|
go(); // Warm up allocations.
|
|
b.iter(go);
|
|
b.bytes = n as u64;
|
|
}
|
|
fn make_monster(mut monster: MapBuilder) {
|
|
monster.push("type", "great orc");
|
|
monster.push("age", 100u8);
|
|
monster.push("name", "Mr. Orc");
|
|
monster.push("coins", &[1, 25, 50, 100, 250]);
|
|
monster.push("color", &[255u8, 0, 0, 0]);
|
|
{
|
|
let mut weapons = monster.start_vector("weapons");
|
|
{
|
|
let mut hammer = weapons.start_map();
|
|
hammer.push("name", "hammer");
|
|
hammer.push("damage type", "crush");
|
|
hammer.push("damage", 20);
|
|
}
|
|
{
|
|
let mut axe = weapons.start_map();
|
|
axe.push("name", "Great Axe");
|
|
axe.push("damage type", "slash");
|
|
axe.push("damage", 30);
|
|
}
|
|
}
|
|
{
|
|
let mut sounds = monster.start_vector("sounds");
|
|
sounds.push("grr");
|
|
sounds.push("rawr");
|
|
sounds.push("muahaha");
|
|
}
|
|
}
|
|
fn serialize_monsters(b: &mut Bencher) {
|
|
let mut builder = Builder::default();
|
|
let mut n = 0;
|
|
let mut go = || {
|
|
let mut monsters = builder.start_vector();
|
|
for _ in 0..100 {
|
|
make_monster(monsters.start_map())
|
|
}
|
|
monsters.end_vector();
|
|
n = builder.view().len();
|
|
};
|
|
go(); // Warm up allocations.
|
|
b.iter(go);
|
|
b.bytes = n as u64;
|
|
}
|
|
fn validate_monster(r: MapReader) {
|
|
assert_eq!(r.idx("type").as_str(), "great orc");
|
|
assert_eq!(r.idx("age").as_u8(), 100);
|
|
assert_eq!(r.idx("name").as_str(), "Mr. Orc");
|
|
assert!(r
|
|
.idx("coins")
|
|
.as_vector()
|
|
.iter()
|
|
.map(|c| c.as_i16())
|
|
.eq([1, 25, 50, 100, 250].iter().cloned()));
|
|
assert!(r
|
|
.idx("color")
|
|
.as_vector()
|
|
.iter()
|
|
.map(|c| c.as_u8())
|
|
.eq([255, 0, 0, 0].iter().cloned()));
|
|
|
|
let weapons = r.idx("weapons").as_vector();
|
|
assert_eq!(weapons.len(), 2);
|
|
|
|
let hammer = weapons.idx(0).as_map();
|
|
assert_eq!(hammer.idx("name").as_str(), "hammer");
|
|
assert_eq!(hammer.idx("damage type").as_str(), "crush");
|
|
assert_eq!(hammer.idx("damage").as_u64(), 20);
|
|
|
|
let axe = weapons.idx(1).as_map();
|
|
assert_eq!(axe.idx("name").as_str(), "Great Axe");
|
|
assert_eq!(axe.idx("damage type").as_str(), "slash");
|
|
assert_eq!(axe.idx("damage").as_u64(), 30);
|
|
|
|
assert!(r
|
|
.idx("sounds")
|
|
.as_vector()
|
|
.iter()
|
|
.map(|s| s.as_str())
|
|
.eq(["grr", "rawr", "muahaha"].iter().cloned()));
|
|
}
|
|
fn read_monsters(b: &mut Bencher) {
|
|
let mut builder = Builder::default();
|
|
let mut monsters = builder.start_vector();
|
|
for _ in 0..100 {
|
|
make_monster(monsters.start_map());
|
|
}
|
|
monsters.end_vector();
|
|
b.bytes = builder.view().len() as u64;
|
|
let go = || {
|
|
let r = Reader::get_root(builder.view()).unwrap().as_vector();
|
|
assert_eq!(r.len(), 100);
|
|
for i in 0..100 {
|
|
validate_monster(r.idx(i).as_map());
|
|
}
|
|
};
|
|
b.iter(go);
|
|
}
|
|
|
|
benchmark_group!(
|
|
benches,
|
|
push_vec_u64_to_map,
|
|
push_vec_u64_to_map_reused,
|
|
push_vec_u64_to_map_direct,
|
|
push_vec_u64_to_map_direct_reused,
|
|
push_vec_without_indirect,
|
|
push_vec_with_indirect,
|
|
hundred_maps,
|
|
hundred_maps_pooled,
|
|
serialize_monsters,
|
|
read_monsters,
|
|
);
|
|
benchmark_main!(benches);
|