blocks: split macro into multiple functions, fast! (#267)

Improves fix for #184, whereas #255 reduced optimizations,
we now address the underlying compiler limitation and split out
the one massive lazy_static! initialization function, into
one function per block in the block_registration_functions module.

Previous build time, with opt-level=1:

% time cargo build --release
   Compiling steven_blocks v0.0.1
    Finished release [optimized] target(s) in 21.24s
cargo build --release  31.80s user 0.71s system 152% cpu 21.276 total

With this change, opt-level=3 and the function splitting fix:

% time cargo build --release
   Compiling steven_blocks v0.0.1
    Finished release [optimized] target(s) in 30.80s
cargo build --release  40.26s user 0.86s system 133% cpu 30.850 total

Full optimizations are expectedly slightly slower, but this is still
much much _much_ faster than before this refactoring, where this crate
would take up to an unbelievable 5 hours (and tens of GB of RAM). Long
story short, we're now back to full optimizations and stable Rust.

Thanks to dtolnay on the Rust programming language forum for suggesting
this technique, https://users.rust-lang.org/t/5-hours-to-compile-macro-what-can-i-do/36508/2
This commit is contained in:
iceiix 2020-01-09 20:08:28 -08:00 committed by GitHub
parent 643de31073
commit 87e0726f3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 57 additions and 41 deletions

View File

@ -12,10 +12,9 @@ tasks:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup.sh
sh ./rustup.sh -y
source $HOME/.cargo/env
rustup install nightly
cargo +nightly --version
cargo --version
- build: |
source $HOME/.cargo/env
cd stevenarella
cargo +nightly build
cargo +nightly test
cargo build
cargo test

View File

@ -17,9 +17,6 @@ path = "src/main.rs"
# Use an -O1 optimization level strikes a good compromise between build and program performance.
opt-level = 1
[profile.release.package.steven_blocks]
opt-level = 1
[dependencies]
cfg-if = "0.1.9"
wasm-bindgen = "0.2.44"

View File

@ -66,7 +66,7 @@ The Visual Studio 2017 Redistributable is required to run these builds.
## Building
Requires Rust nightly version `rustc 1.42.0-nightly (760ce94c6 2020-01-04)` or newer to build.
Requires Rust stable version 1.40.0 or newer to build.
Compile and run:
```bash

View File

@ -21,7 +21,7 @@ build_script:
appveyor AddMessage "Platform rust: %RUST_INSTALL%"
appveyor DownloadFile "https://static.rust-lang.org/dist/rust-nightly-%RUST_INSTALL%.exe" -FileName rust-install.exe
appveyor DownloadFile "https://static.rust-lang.org/dist/rust-1.40.0-%RUST_INSTALL%.exe" -FileName rust-install.exe
"./rust-install.exe" /VERYSILENT /NORESTART /DIR="C:\Rust\"

View File

@ -4,9 +4,6 @@ version = "0.0.1"
authors = [ "Thinkofdeath <thinkofdeath@spigotmc.org>" ]
edition = "2018"
[profile.release]
opt-level = 1
[dependencies]
lazy_static = "1.4.0"
cgmath = "0.17.0"

View File

@ -288,15 +288,18 @@ macro_rules! define_blocks {
}
}
lazy_static! {
static ref VANILLA_ID_MAP: VanillaIDMap = {
let mut blocks_flat = vec![];
let mut blocks_hier = vec![];
let mut blocks_modded: HashMap<String, [Option<Block>; 16]> = HashMap::new();
let mut flat_id = 0;
let mut last_internal_id = 0;
let mut hier_block_id = 0;
$({
mod block_registration_functions {
use super::*;
$(
#[allow(non_snake_case)]
pub fn $name(
blocks_flat: &mut Vec<Option<Block>>,
blocks_hier: &mut Vec<Option<Block>>,
blocks_modded: &mut HashMap<String, [Option<Block>; 16]>,
flat_id: &mut usize,
last_internal_id: &mut usize,
hier_block_id: &mut usize,
) {
#[allow(non_camel_case_types, dead_code)]
struct CombinationIter<$($fname),*> {
first: bool,
@ -391,28 +394,28 @@ macro_rules! define_blocks {
let hier_data: Option<usize> = block.get_hierarchical_data();
if let Some(modid) = block.get_modid() {
let hier_data = hier_data.unwrap();
if !blocks_modded.contains_key(modid) {
blocks_modded.insert(modid.to_string(), [None; 16]);
if !(*blocks_modded).contains_key(modid) {
(*blocks_modded).insert(modid.to_string(), [None; 16]);
}
let block_from_data = blocks_modded.get_mut(modid).unwrap();
let block_from_data = (*blocks_modded).get_mut(modid).unwrap();
block_from_data[hier_data] = Some(block);
continue
}
let vanilla_id =
if let Some(hier_data) = hier_data {
if internal_id != last_internal_id {
hier_block_id += 1;
if internal_id != *last_internal_id {
*hier_block_id += 1;
}
last_internal_id = internal_id;
Some((hier_block_id << 4) + hier_data)
*last_internal_id = internal_id;
Some((*hier_block_id << 4) + hier_data)
} else {
None
};
let offset = block.get_flat_offset();
if let Some(offset) = offset {
let id = flat_id + offset;
let id = *flat_id + offset;
/*
if let Some(vanilla_id) = vanilla_id {
debug!("{} block state = {:?} hierarchical {}:{} offset={}", id, block, vanilla_id >> 4, vanilla_id & 0xF, offset);
@ -424,17 +427,17 @@ macro_rules! define_blocks {
last_offset = offset as isize;
}
if blocks_flat.len() <= id {
blocks_flat.resize(id + 1, None);
if (*blocks_flat).len() <= id {
(*blocks_flat).resize(id + 1, None);
}
if blocks_flat[id].is_none() {
blocks_flat[id] = Some(block);
if (*blocks_flat)[id].is_none() {
(*blocks_flat)[id] = Some(block);
} else {
panic!(
"Tried to register {:#?} to {} but {:#?} was already registered",
block,
id,
blocks_flat[id]
(*blocks_flat)[id]
);
}
}
@ -446,17 +449,17 @@ macro_rules! define_blocks {
}
*/
if blocks_hier.len() <= vanilla_id {
blocks_hier.resize(vanilla_id + 1, None);
if (*blocks_hier).len() <= vanilla_id {
(*blocks_hier).resize(vanilla_id + 1, None);
}
if blocks_hier[vanilla_id].is_none() {
blocks_hier[vanilla_id] = Some(block);
if (*blocks_hier)[vanilla_id].is_none() {
(*blocks_hier)[vanilla_id] = Some(block);
} else {
panic!(
"Tried to register {:#?} to {} but {:#?} was already registered",
block,
vanilla_id,
blocks_hier[vanilla_id]
(*blocks_hier)[vanilla_id]
);
}
}
@ -464,9 +467,29 @@ macro_rules! define_blocks {
#[allow(unused_assignments)]
{
flat_id += (last_offset + 1) as usize;
*flat_id += (last_offset + 1) as usize;
}
})+
}
)+
}
lazy_static! {
static ref VANILLA_ID_MAP: VanillaIDMap = {
let mut blocks_flat = vec![];
let mut blocks_hier = vec![];
let mut blocks_modded: HashMap<String, [Option<Block>; 16]> = HashMap::new();
let mut flat_id = 0;
let mut last_internal_id = 0;
let mut hier_block_id = 0;
$(
block_registration_functions::$name(&mut blocks_flat,
&mut blocks_hier,
&mut blocks_modded,
&mut flat_id,
&mut last_internal_id,
&mut hier_block_id);
)+
VanillaIDMap { flat: blocks_flat, hier: blocks_hier, modded: blocks_modded }
};