Skip to content
This repository has been archived by the owner on Aug 15, 2021. It is now read-only.

Compilation is very slow, especially for release builds #189

Open
Timmmm opened this issue May 12, 2020 · 1 comment
Open

Compilation is very slow, especially for release builds #189

Timmmm opened this issue May 12, 2020 · 1 comment

Comments

@Timmmm
Copy link

Timmmm commented May 12, 2020

The example file below contains a load of serde-derive'd structs and code to load them using serde_cbor. I compiled it in release & debug, and then again after changing serde_cbor to serde_json to get the following compile times:

Debug Release
serde_json 5.0s 13.3s
serde_cbor 11.2s 53.9s

As you can see it is a lot slower than serde_json, and the release build is unusably slow. By looking at the Rust and LLVM profiling outputs I found that most of the time is spent in LLVM and mostly in optimising serde_cbor-related functions.

I compared the output of cargo expand and as expected the only line that changes is serde_cbor/json::from_reader().

To get the LLVM timings run this command:

cargo +nightly rustc --release -- -Zself-profile -Zno-parallel-llvm -Zllvm-time-trace

And then open the resulting llvm_timings.json in about:tracing.

image

If you zoom in and click on the OptFunction blocks it tells you which function it is optimising.

image

From clicking around randomly the most common one is serde_cbor::de::Deserializer<R>::parse_value function (with various hashes at the end), but there's also parse_str, parse_map, recursion_checked, etc. Kind of makes sense because parse_value has that big map but honesty I can't see why it should take so long.

Here's my test code:

#![allow(non_snake_case)]
#![allow(non_camel_case_types)]

use serde::Deserialize;
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use anyhow::Result;

#[derive(Deserialize, Debug)]
pub struct CA {
  pub a: CB,
  pub b: CC,
  pub c: HashMap<String, f64>,
  pub d: Vec<CD>,

  pub e: CE,
  pub f: CF,

  pub g: Vec<CG>,
  pub h: Vec<u32>,
  pub i: Vec<u32>,

  pub j: CJ,
  pub k: CH,
  pub l: CL,

  pub n: Memory,

  pub o: Option<CO>,
}

#[derive(Deserialize, Debug)]
pub struct CO {
  pub a: String,
  pub b: String,
  pub c: u32,
  pub d: u32,
  pub e: u32,
}

#[derive(Deserialize, Debug)]
pub struct CB {
  pub a: u64,
  pub b: u64,
  pub c: u64,
  pub d: u64,
}

#[derive(Deserialize, Debug)]
pub struct CD {
  pub a: String,
  pub b: u64,
  pub c: String,
  pub d: String,
  pub e: u32,
  pub f: u32,
  pub g: u64,
}

#[derive(Deserialize, Debug)]
pub struct CJ {
  pub a: Vec<Vec<u64>>,
  pub b: Vec<Vec<u64>>,
  pub c: Option<Vec<Vec<u64>>>,
  pub d: Vec<Vec<u64>>,
  pub e: Option<Vec<String>>,
  pub f: Option<Vec<String>>,

}

#[derive(Deserialize, Debug)]
pub struct CL {
  pub a: Vec<Vec<u64>>,
  pub b: Vec<Vec<u64>>,
  pub c: Vec<Vec<u64>>,
  pub d: Option<Vec<HashMap<String, String>>>,
  pub e: Option<Vec<HashMap<String, String>>>,

}

#[derive(Deserialize, Debug)]
pub struct CH {
  pub a: Vec<Vec<u64>>,
  pub b: Vec<Vec<u64>>,
  pub c: Vec<Vec<u64>>,
  pub d: Option<Vec<String>>,
  pub e: Option<Vec<String>>,

}

#[derive(Deserialize, Debug)]
pub struct CE {
  pub a: Vec<String>,
  pub b: Vec<Vec<u32>>,
  pub c: Vec<Vec<u32>>,
  pub d: Option<CycleEstimates>,
}

#[derive(Deserialize, Debug)]
pub struct CycleEstimates {
  pub a: Vec<Vec<u64>>,
  pub b: Vec<Vec<u64>>,
  pub c: Vec<Vec<u64>>,
}

#[derive(Deserialize, Debug)]
pub struct CC {
  pub a: u64,
  pub b: u64,
  pub c: f64,
  pub d: u64,
  pub e: u64,
  pub f: u64,
  pub g: Vec<u64>,
  pub h: u64,
  pub i: u64,
  pub j: TT,
}

#[derive(Deserialize, Debug)]
pub enum TT {
  A,
  B,
  C,
}

#[derive(Deserialize, Debug)]
pub struct CF {
  pub a: Vec<String>,
  pub b: Vec<u64>,
}

#[derive(Deserialize, Debug)]
pub struct Memory {
  pub a: DA,
  pub b: DB,
  pub c: DC,
  pub d: DD,
  pub e: DE,
}

#[derive(Deserialize, Debug)]
pub struct DA {
  pub a1: AA,
  pub a2: AA,
  pub a3: AA,
  pub a4: AA,
  pub a5: AA,
  pub a6: AA,
  pub a7: AA,
  pub a8: AA,
  pub a9: AA,
  pub a10: AA,
  pub a11: AA,
  pub a12: AA,
  pub a13: AA,
  pub a14: AA,
  pub a15: AA,
  pub a16: AA,
  pub a17: AA,
  pub a18: AA,
  pub a19: AA,
  pub a20: AA,
  pub a21: AA,
  pub a22: AA,
}

#[derive(Deserialize, Debug)]
pub struct AA {
  pub a: XS,
  pub b: XS,
  pub c: XS,
  pub d: Vec<u32>,
}

#[derive(Deserialize, Debug)]
pub struct XS {
  pub a: Vec<u32>,
  pub b: Vec<u32>,
}

#[derive(Deserialize, Debug)]
pub struct DB {
  pub a1: Vec<Vec<u32>>,
  pub a2: Vec<Vec<u32>>,
  pub a3: Vec<Vec<u32>>,
  pub a4: Vec<Vec<u32>>,
  pub a5: Vec<Vec<u32>>,
  pub a6: Vec<Vec<u32>>,
  pub a7: Vec<Vec<u32>>,
  pub a8: Vec<Vec<u32>>,
}

#[derive(Deserialize, Debug)]
pub struct DC {
  pub a1: Vec<u32>,
  pub a2: Vec<u32>,
  pub a3: Vec<u32>,
  pub a4: Vec<u32>,
  pub a5: Vec<u32>,
  pub a6: Vec<u32>,
  pub a7: Vec<u32>,
  pub a8: Vec<u32>,
}

#[derive(Deserialize, Debug)]
pub struct DD {
  pub a1: Vec<Vec<u32>>,
  pub a2: Vec<Vec<u32>>,
  pub a3: Vec<Vec<u32>>,
  pub a4: Vec<Vec<u32>>,
  pub a5: Vec<Vec<u32>>,
  pub a6: Vec<Vec<u32>>,
  pub a7: Vec<Vec<u32>>,
  pub a8: Vec<Vec<u32>>,
}

#[derive(Deserialize, Debug)]
pub struct DE {
  pub a: AL,
  pub b: Vec<u64>,
  pub e: NAL,
}

#[derive(Deserialize, Debug)]
pub struct AL {
  pub a: Vec<u32>,
  pub b: HashMap<String, VDE>,
}

#[derive(Deserialize, Debug)]
pub struct VDE {
  pub a: u64,
  pub b: Vec<u32>,
}

#[derive(Deserialize, Debug)]
pub struct NAL {
  pub a: Vec<u32>,
  pub b: Vec<CGDE>,
}

#[derive(Deserialize, Debug)]
pub struct CGDE {
  pub a: u64,
  pub b: Vec<u32>,
  pub c: usize,
  pub d: HashMap<String, VDE>,
  pub e: Vec<CGDE>,
}


#[derive(Deserialize, Debug)]
#[serde(tag = "type")]
pub enum CG {
  Se(Se),
  Sh(Sh),
  Sw(Sw),
  Re(Re),
  ReWh(ReWh),
  If(If),
  IfElse(IfElse),
  Call(Call),
  OTE(OTE),
  DX(DX),
  GX(GX),
  SCo(SCo),
  CSS(CSS),
  Sync(Sync),
  SLC(SLC),
  SLCFV(SLCFV),
  GLC(GLC),
  ATF(ATF),
  ATVE(ATVE),
  WU(WU),
  SSSS(SSSS),
  Sans(Sans),
  Sans2(Sans2),
  Tif(Tif),
}

#[derive(Deserialize, Debug)]
pub struct Se {
  pub c: Vec<usize>,
}

#[derive(Deserialize, Debug)]
pub struct Sh {
  pub c: Vec<usize>, // 1 child
}

#[derive(Deserialize, Debug)]
pub struct Sw {
  pub c: Vec<usize>,
}

#[derive(Deserialize, Debug)]
pub struct Re {
  pub c: Vec<usize>, // 1 child
}

#[derive(Deserialize, Debug)]
pub struct ReWh {
  pub c: Vec<usize>, // 1st child is condition, 2nd is body.
}

#[derive(Deserialize, Debug)]
pub struct If {
  pub c: Vec<usize>, // 1st child is true body, 2nd is false body.
}

#[derive(Deserialize, Debug)]
pub struct IfElse {
  pub c: Vec<usize>,
}

#[derive(Deserialize, Debug)]
pub struct Call {
  pub a: usize,
}

#[derive(Deserialize, Debug)]
pub struct OTE {
  pub b: usize,
}

#[derive(Deserialize, Debug)]
pub struct DX {
  pub a: usize,
  pub b: String,
}

#[derive(Deserialize, Debug)]
pub struct GX {
  pub a: usize,
  pub b: String,
}

#[derive(Deserialize, Debug)]
pub struct SCo {
  pub a: usize,
}

#[derive(Deserialize, Debug)]
pub struct CSS {
  pub a: usize,
}

#[derive(Deserialize, Debug)]
pub struct Sync {
  pub a: SyTy,
}

#[derive(Deserialize, Debug)]
pub struct SLC {
}

#[derive(Deserialize, Debug)]
pub struct SLCFV {
}

#[derive(Deserialize, Debug)]
pub struct GLC {
}

#[derive(Deserialize, Debug)]
pub struct ATF {
}

#[derive(Deserialize, Debug)]
pub struct ATVE {
}

#[derive(Deserialize, Debug)]
pub struct WU {
}

#[derive(Deserialize, Debug)]
pub struct SSSS {
}

#[derive(Deserialize, Debug)]
pub struct Sans {
  pub a: Vec<u32>,
}

#[derive(Deserialize, Debug)]
pub struct Sans2 {
  pub a: Vec<u32>,
}

#[derive(Deserialize, Debug)]
pub struct Tif {
  pub c: Vec<usize>,
}

#[derive(Deserialize, Debug)]
pub enum SyTy {
  Internal,
  External,
}

pub fn read_file(
  filename: &Path,
) -> Result<CA> {

  let file = File::open(filename)?;

  let reader = BufReader::new(file);

  Ok(serde_cbor::from_reader(reader)?)
}

fn main() -> Result<()> {
    let gp = read_file(Path::new("foo"))?;
    dbg!(gp);
    Ok(())
}
@jzrake
Copy link

jzrake commented Feb 15, 2021

I'm just commenting on this issue to say that I've seen the same problem: serde_cbor consumes 60-70% of my code's build time. I can't blame the author, he is busy and according to this post never meant to maintain this project at serious level. I have switched to ciborium, which is newer and perhaps more risky, but has normal compilation times.

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

No branches or pull requests

2 participants