Merge commit '0f6aab9da6fe982218a01f4a5b896e65fcced437' as 'third_party/flatbuffers'
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2020 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*/
|
||||
|
||||
#[macro_use]
|
||||
extern crate bencher;
|
||||
extern crate flatbuffers;
|
||||
extern crate flexbuffers;
|
||||
|
||||
mod flatbuffers_benchmarks;
|
||||
mod flexbuffers_benchmarks;
|
||||
|
||||
#[allow(dead_code, unused_imports)]
|
||||
#[path = "../../monster_test/mod.rs"]
|
||||
mod monster_test_generated;
|
||||
pub use monster_test_generated::my_game;
|
||||
|
||||
benchmark_main!(
|
||||
flatbuffers_benchmarks::benches,
|
||||
flexbuffers_benchmarks::benches
|
||||
);
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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::{benchmark_group, Bencher};
|
||||
use flatbuffers;
|
||||
|
||||
#[allow(dead_code, unused_imports)]
|
||||
#[path = "../../monster_test/mod.rs"]
|
||||
mod monster_test_generated;
|
||||
pub use monster_test_generated::my_game;
|
||||
|
||||
fn traverse_canonical_buffer(bench: &mut Bencher) {
|
||||
let owned_data = {
|
||||
let mut builder = &mut flatbuffers::FlatBufferBuilder::new();
|
||||
create_serialized_example_with_generated_code(&mut builder, true);
|
||||
builder.finished_data().to_vec()
|
||||
};
|
||||
let data = &owned_data[..];
|
||||
let n = data.len() as u64;
|
||||
bench.iter(|| {
|
||||
traverse_serialized_example_with_generated_code(data);
|
||||
});
|
||||
bench.bytes = n;
|
||||
}
|
||||
|
||||
fn create_canonical_buffer_then_reset(bench: &mut Bencher) {
|
||||
let mut builder = &mut flatbuffers::FlatBufferBuilder::new();
|
||||
// warmup
|
||||
create_serialized_example_with_generated_code(&mut builder, true);
|
||||
let n = builder.finished_data().len() as u64;
|
||||
builder.reset();
|
||||
|
||||
bench.iter(|| {
|
||||
let _ = create_serialized_example_with_generated_code(&mut builder, true);
|
||||
builder.reset();
|
||||
});
|
||||
|
||||
bench.bytes = n;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn create_serialized_example_with_generated_code(
|
||||
builder: &mut flatbuffers::FlatBufferBuilder,
|
||||
finish: bool,
|
||||
) -> usize {
|
||||
let s0 = builder.create_string("test1");
|
||||
let s1 = builder.create_string("test2");
|
||||
let t0_name = builder.create_string("Barney");
|
||||
let t1_name = builder.create_string("Fred");
|
||||
let t2_name = builder.create_string("Wilma");
|
||||
let t0 = my_game::example::Monster::create(
|
||||
builder,
|
||||
&my_game::example::MonsterArgs {
|
||||
hp: 1000,
|
||||
name: Some(t0_name),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let t1 = my_game::example::Monster::create(
|
||||
builder,
|
||||
&my_game::example::MonsterArgs {
|
||||
name: Some(t1_name),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let t2 = my_game::example::Monster::create(
|
||||
builder,
|
||||
&my_game::example::MonsterArgs {
|
||||
name: Some(t2_name),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let mon = {
|
||||
let name = builder.create_string("MyMonster");
|
||||
let fred_name = builder.create_string("Fred");
|
||||
let inventory = builder.create_vector(&[0u8, 1, 2, 3, 4]);
|
||||
let test4 = builder.create_vector(&[
|
||||
my_game::example::Test::new(10, 20),
|
||||
my_game::example::Test::new(30, 40),
|
||||
]);
|
||||
let pos = my_game::example::Vec3::new(
|
||||
1.0,
|
||||
2.0,
|
||||
3.0,
|
||||
3.0,
|
||||
my_game::example::Color::Green,
|
||||
&my_game::example::Test::new(5i16, 6i8),
|
||||
);
|
||||
let args = my_game::example::MonsterArgs {
|
||||
hp: 80,
|
||||
mana: 150,
|
||||
name: Some(name),
|
||||
pos: Some(&pos),
|
||||
test_type: my_game::example::Any::Monster,
|
||||
test: Some(
|
||||
my_game::example::Monster::create(
|
||||
builder,
|
||||
&my_game::example::MonsterArgs {
|
||||
name: Some(fred_name),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.as_union_value(),
|
||||
),
|
||||
inventory: Some(inventory),
|
||||
test4: Some(test4),
|
||||
testarrayofstring: Some(builder.create_vector(&[s0, s1])),
|
||||
testarrayoftables: Some(builder.create_vector(&[t0, t1, t2])),
|
||||
..Default::default()
|
||||
};
|
||||
my_game::example::Monster::create(builder, &args)
|
||||
};
|
||||
if finish {
|
||||
my_game::example::finish_monster_buffer(builder, mon);
|
||||
}
|
||||
|
||||
builder.finished_data().len()
|
||||
|
||||
// make it do some work
|
||||
// if builder.finished_data().len() == 0 { panic!("bad benchmark"); }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn blackbox<T>(t: T) -> T {
|
||||
// encapsulate this in case we need to turn it into a noop
|
||||
bencher::black_box(t)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn traverse_serialized_example_with_generated_code(bytes: &[u8]) {
|
||||
let m = unsafe { my_game::example::root_as_monster_unchecked(bytes) };
|
||||
blackbox(m.hp());
|
||||
blackbox(m.mana());
|
||||
blackbox(m.name());
|
||||
let pos = m.pos().unwrap();
|
||||
blackbox(pos.x());
|
||||
blackbox(pos.y());
|
||||
blackbox(pos.z());
|
||||
blackbox(pos.test1());
|
||||
blackbox(pos.test2());
|
||||
let pos_test3 = pos.test3();
|
||||
blackbox(pos_test3.a());
|
||||
blackbox(pos_test3.b());
|
||||
blackbox(m.test_type());
|
||||
let table2 = m.test().unwrap();
|
||||
let monster2 = unsafe { my_game::example::Monster::init_from_table(table2) };
|
||||
blackbox(monster2.name());
|
||||
blackbox(m.inventory());
|
||||
blackbox(m.test4());
|
||||
let testarrayoftables = m.testarrayoftables().unwrap();
|
||||
blackbox(testarrayoftables.get(0).hp());
|
||||
blackbox(testarrayoftables.get(0).name());
|
||||
blackbox(testarrayoftables.get(1).name());
|
||||
blackbox(testarrayoftables.get(2).name());
|
||||
let testarrayofstring = m.testarrayofstring().unwrap();
|
||||
blackbox(testarrayofstring.get(0));
|
||||
blackbox(testarrayofstring.get(1));
|
||||
}
|
||||
|
||||
fn create_string_10(bench: &mut Bencher) {
|
||||
let builder = &mut flatbuffers::FlatBufferBuilder::with_capacity(1 << 20);
|
||||
let mut i = 0;
|
||||
bench.iter(|| {
|
||||
builder.create_string("foobarbaz"); // zero-terminated -> 10 bytes
|
||||
i += 1;
|
||||
if i == 10000 {
|
||||
builder.reset();
|
||||
i = 0;
|
||||
}
|
||||
});
|
||||
|
||||
bench.bytes = 10;
|
||||
}
|
||||
|
||||
fn create_string_100(bench: &mut Bencher) {
|
||||
let builder = &mut flatbuffers::FlatBufferBuilder::with_capacity(1 << 20);
|
||||
let s_owned = (0..99).map(|_| "x").collect::<String>();
|
||||
let s: &str = &s_owned;
|
||||
|
||||
let mut i = 0;
|
||||
bench.iter(|| {
|
||||
builder.create_string(s); // zero-terminated -> 100 bytes
|
||||
i += 1;
|
||||
if i == 1000 {
|
||||
builder.reset();
|
||||
i = 0;
|
||||
}
|
||||
});
|
||||
|
||||
bench.bytes = s.len() as u64;
|
||||
}
|
||||
|
||||
fn create_byte_vector_100_naive(bench: &mut Bencher) {
|
||||
let builder = &mut flatbuffers::FlatBufferBuilder::with_capacity(1 << 20);
|
||||
let v_owned = (0u8..100).map(|i| i).collect::<Vec<u8>>();
|
||||
let v: &[u8] = &v_owned;
|
||||
|
||||
let mut i = 0;
|
||||
bench.iter(|| {
|
||||
builder.create_vector(v); // zero-terminated -> 100 bytes
|
||||
i += 1;
|
||||
if i == 10000 {
|
||||
builder.reset();
|
||||
i = 0;
|
||||
}
|
||||
});
|
||||
|
||||
bench.bytes = v.len() as u64;
|
||||
}
|
||||
|
||||
fn create_byte_vector_100_optimal(bench: &mut Bencher) {
|
||||
let builder = &mut flatbuffers::FlatBufferBuilder::with_capacity(1 << 20);
|
||||
let v_owned = (0u8..100).map(|i| i).collect::<Vec<u8>>();
|
||||
let v: &[u8] = &v_owned;
|
||||
|
||||
let mut i = 0;
|
||||
bench.iter(|| {
|
||||
builder.create_vector(v);
|
||||
i += 1;
|
||||
if i == 10000 {
|
||||
builder.reset();
|
||||
i = 0;
|
||||
}
|
||||
});
|
||||
|
||||
bench.bytes = v.len() as u64;
|
||||
}
|
||||
|
||||
fn create_many_tables(bench: &mut Bencher) {
|
||||
let builder = &mut flatbuffers::FlatBufferBuilder::with_capacity(1 << 20);
|
||||
// We test vtable overhead by making many unique tables of up to 16 fields of u8s.
|
||||
bench.iter(|| {
|
||||
for i in 0..(1u16 << 10) {
|
||||
let t = builder.start_table();
|
||||
for j in 0..15 {
|
||||
if i & (1 << j) == 1 {
|
||||
builder.push_slot_always(i * 2, 42u8);
|
||||
}
|
||||
}
|
||||
builder.end_table(t);
|
||||
}
|
||||
builder.reset();
|
||||
});
|
||||
bench.bytes = 1 << 15;
|
||||
}
|
||||
|
||||
benchmark_group!(
|
||||
benches,
|
||||
create_byte_vector_100_naive,
|
||||
create_byte_vector_100_optimal,
|
||||
traverse_canonical_buffer,
|
||||
create_canonical_buffer_then_reset,
|
||||
create_string_10,
|
||||
create_string_100,
|
||||
create_many_tables,
|
||||
);
|
||||
+295
@@ -0,0 +1,295 @@
|
||||
// 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::{benchmark_group, benchmark_main, 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<&[u8]>) {
|
||||
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);
|
||||
Reference in New Issue
Block a user