Compilation is very slow, especially for release builds #189
Description
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
.
If you zoom in and click on the OptFunction
blocks it tells you which function it is optimising.
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(())
}