Benchmark rust in CI with iai, not criterion (#1937)

* Rename cargo-criterion to cargo-bench

* Use iai not criterion in CI

We want to benchmark the KCL parser and tokenizer to make sure we don't
accidentally slow them down. Generally Rust projects use Criterion to
benchmark code. Criterion runs your functions a few thousand times to
get reliable wall-clock measurements.

This is good for locally benchmarking but bad for benchmarking in CI.
Why? Because in CI, you're running a container on some shared VM, so
wall-clock time might have a lot of interference from noisy neighbours.
Also, your benchmarks take a long time to run and eat up paid CI minutes.

A better approach for benchmarking in CI is to just count the number of
CPU instructions executed. This correlates with wall-clock time, but it
only needs to run the function once, so it takes much less time. It also
isn't changed by any noisy neighbours running on the same VM or hardware.

This PR adds a new benchmark suite which counts instructions using `iai`,
from the creator of criterion. He says iai and criterion complement each
other nicely. We can run criterion locally and run iai in CI.

* Update image in markdown docs
This commit is contained in:
Adam Chalmers
2024-04-04 09:50:34 -05:00
committed by GitHub
parent b32295e1d9
commit 594e888c12
7 changed files with 72 additions and 8 deletions

View File

@ -7,23 +7,23 @@ on:
- '**/Cargo.toml'
- '**/Cargo.lock'
- '**/rust-toolchain.toml'
- .github/workflows/cargo-criterion.yml
- .github/workflows/cargo-bench.yml
pull_request:
paths:
- '**.rs'
- '**/Cargo.toml'
- '**/Cargo.lock'
- '**/rust-toolchain.toml'
- .github/workflows/cargo-criterion.yml
- .github/workflows/cargo-bench.yml
workflow_dispatch:
permissions: read-all
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
name: cargo criterion
name: cargo bench
jobs:
cargocriterion:
name: cargo criterion
cargo-bench:
name: Benchmark with iai
runs-on: ubuntu-latest-8-cores
steps:
- uses: actions/checkout@v4
@ -31,10 +31,12 @@ jobs:
- name: Install dependencies
run: |
cargo install cargo-criterion
sudo apt update
sudo apt install -y valgrind
- name: Rust Cache
uses: Swatinem/rust-cache@v2.6.1
- name: Benchmark kcl library
shell: bash
run: |-
cd src/wasm-lib/kcl; cargo criterion
cd src/wasm-lib/kcl; cargo bench -- iai

File diff suppressed because one or more lines are too long

View File

@ -1646,6 +1646,12 @@ dependencies = [
"tokio-native-tls",
]
[[package]]
name = "iai"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71a816c97c42258aa5834d07590b718b4c9a598944cd39a52dc25b351185d678"
[[package]]
name = "iana-time-zone"
version = "0.1.60"
@ -1877,6 +1883,7 @@ dependencies = [
"expectorate",
"futures",
"gltf-json",
"iai",
"image",
"insta",
"itertools 0.12.1",

View File

@ -71,6 +71,7 @@ base64 = "0.22.0"
convert_case = "0.6.0"
criterion = "0.5.1"
expectorate = "1.1.0"
iai = "0.1"
image = "0.24.9"
insta = { version = "1.38.0", features = ["json"] }
itertools = "0.12.1"
@ -79,5 +80,9 @@ tokio = { version = "1.37.0", features = ["rt-multi-thread", "macros", "time"] }
twenty-twenty = "0.7.0"
[[bench]]
name = "compiler_benchmark"
name = "compiler_benchmark_criterion"
harness = false
[[bench]]
name = "compiler_benchmark_iai"
harness = false

View File

@ -0,0 +1,50 @@
use iai::black_box;
pub fn parse(program: &str) {
let tokens = kcl_lib::token::lexer(program);
let tok = tokens.clone();
let parser = kcl_lib::parser::Parser::new(tok.clone());
black_box(parser.ast().unwrap());
}
fn lex_kitt() {
black_box(kcl_lib::token::lexer(KITT_PROGRAM));
}
fn lex_pipes() {
black_box(kcl_lib::token::lexer(PIPES_PROGRAM));
}
fn lex_cube() {
black_box(kcl_lib::token::lexer(CUBE_PROGRAM));
}
fn lex_math() {
black_box(kcl_lib::token::lexer(MATH_PROGRAM));
}
fn parse_kitt() {
parse(KITT_PROGRAM)
}
fn parse_pipes() {
parse(PIPES_PROGRAM)
}
fn parse_cube() {
parse(CUBE_PROGRAM)
}
fn parse_math() {
parse(MATH_PROGRAM)
}
iai::main! {
lex_kitt,
lex_pipes,
lex_cube,
lex_math,
parse_kitt,
parse_pipes,
parse_cube,
parse_math,
}
const KITT_PROGRAM: &str = include_str!("../../tests/executor/inputs/kittycad_svg.kcl");
const PIPES_PROGRAM: &str = include_str!("../../tests/executor/inputs/pipes_on_pipes.kcl");
const CUBE_PROGRAM: &str = include_str!("../../tests/executor/inputs/cube.kcl");
const MATH_PROGRAM: &str = include_str!("../../tests/executor/inputs/math.kcl");

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 95 KiB