Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Rust) FlexBuffers an order of magnitude slower than JSON serde for flexbuffers example #8460

Open
danthegoodman1 opened this issue Dec 23, 2024 · 0 comments

Comments

@danthegoodman1
Copy link

danthegoodman1 commented Dec 23, 2024

I modified the example at https://github.com/google/flatbuffers/blob/master/samples/sample_flexbuffers_serde.rs to the following code, and observed the following times with cargo run --release on an m3 max mbp rustc 1.83.0 (90b35a623 2024-11-26):

FlexBuffers direct access: 333ns
Comparison:
FlexBuffers size: 2227 bytes
JSON size: 2230 bytes

Timing:
FlexBuffers serialization: 83.125µs
FlexBuffers deserialization: 60.917µs
JSON serialization: 8.5µs
JSON deserialization: 19.083µs

main.rs:

use flexbuffers;
use serde;
use serde::{Deserialize, Serialize};

#[derive(Debug, PartialEq, Serialize, Deserialize)]
enum Weapon {
    Fist,
    Equipment { name: String, damage: i32 },
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Color(u8, u8, u8, u8);

#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Monster {
    hp: u32,
    mana: i32,
    enraged: bool,
    weapons: Vec<Weapon>,
    color: Color,
    position: [f64; 3],
    velocity: [f64; 3],
    coins: Vec<u32>,
}

fn main() {
    let monster = Monster {
        hp: 80,
        mana: 200,
        enraged: true,
        color: Color(255, 255, 255, 255),
        position: [0.0; 3],
        velocity: [1.0, 0.0, 0.0],
        weapons: vec![
            Weapon::Fist,
            Weapon::Equipment {
                name: "great axe".to_string(),
                damage: 15,
            },
            Weapon::Equipment {
                name: "hammer".to_string(),
                damage: 5,
            },
        ],
        coins: vec![5; 1000],
    };

    // FlexBuffers serialization
    let start_flex_ser = std::time::Instant::now();
    let mut flex_buf = flexbuffers::FlexbufferSerializer::new();
    monster.serialize(&mut flex_buf).unwrap();
    let flex_ser_time = start_flex_ser.elapsed();
    
    // FlexBuffers deserialization
    let flex_data = flex_buf.view();
    let start_flex_deser = std::time::Instant::now();
    let r = flexbuffers::Reader::get_root(flex_data).unwrap();
    let monster2 = Monster::deserialize(r).unwrap();
    let flex_deser_time = start_flex_deser.elapsed();

    // JSON serialization
    let start_json_ser = std::time::Instant::now();
    let json_data = serde_json::to_vec(&monster).unwrap();
    let json_ser_time = start_json_ser.elapsed();

    // JSON deserialization
    let start_json_deser = std::time::Instant::now();
    let monster3: Monster = serde_json::from_slice(&json_data).unwrap();
    let json_deser_time = start_json_deser.elapsed();

    // Add direct field access test for FlexBuffers
    let start_flex_access = std::time::Instant::now();
    let r = flexbuffers::Reader::get_root(flex_data).unwrap();
    let hp = r.as_map().idx("hp").as_u32();
    let flex_access_time = start_flex_access.elapsed();
    
    println!("FlexBuffers direct access: {:?}", flex_access_time);

    println!("Comparison:");
    println!("FlexBuffers size: {} bytes", flex_data.len());
    println!("JSON size: {} bytes", json_data.len());
    println!("\nTiming:");
    println!("FlexBuffers serialization: {:?}", flex_ser_time);
    println!("FlexBuffers deserialization: {:?}", flex_deser_time);
    println!("JSON serialization: {:?}", json_ser_time);
    println!("JSON deserialization: {:?}", json_deser_time);

    assert_eq!(monster, monster2);
    assert_eq!(monster, monster3);
}

Cargo.toml:

[package]
name = "flex-buffer-testing"
version = "0.1.0"
edition = "2021"

[dependencies]
flexbuffers = "24.12.23"
serde = { version = "1.0.216", features = ["serde_derive"] }
serde_json = "1.0.134"

Is this expected? The size and performance differences to JSON makes it seems like with serde, all benefits are being lost.

I get that you'd still likely access it with something like the direct access I had done above, but the size issue seems a bit strange to me that it's losing so much efficiency. Maybe I was expecting flatbuffers-level, not sure

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant