refactor(benchmark): make the benchmark run faster by using variable measurement time

This commit is contained in:
Boshen 2023-03-05 23:19:51 +08:00
parent 8dfb50642d
commit cb886d8a36
No known key found for this signature in database
GPG key ID: 9C7A8C8AB22BEBD1
3 changed files with 118 additions and 85 deletions

View file

@ -1,39 +1,59 @@
use std::{path::PathBuf, str::FromStr};
use std::{path::PathBuf, str::FromStr, time::Duration};
pub struct Code {
pub url: &'static str,
pub file_name: String,
pub source_text: String,
pub measurement_time: Duration,
}
impl Code {
/// # Errors
pub fn new(measurement_seconds: u64, url: &'static str) -> Result<Self, String> {
let (file_name, source_text) = Self::get_source_text(url)?;
Ok(Self {
url,
file_name,
source_text,
measurement_time: Duration::new(measurement_seconds, 0),
})
}
/// # Errors
/// # Panics
pub fn get_source_text(lib: &str) -> Result<(String, String), String> {
let url = url::Url::from_str(lib).map_err(err_to_string)?;
let segments = url.path_segments().ok_or_else(|| "lib url has no segments".to_string())?;
let filename = segments.last().ok_or_else(|| "lib url has no segments".to_string())?;
let mut file = PathBuf::from_str("target").map_err(err_to_string)?;
file.push(filename);
if let Ok(code) = std::fs::read_to_string(&file) {
println!("[{filename}] - using [{}]", file.display());
Ok((filename.to_string(), code))
} else {
println!("[{filename}] - Downloading [{lib}] to [{}]", file.display());
match ureq::get(lib).call() {
Ok(response) => {
let mut reader = response.into_reader();
let _drop = std::fs::remove_file(&file);
let mut writer = std::fs::File::create(&file).map_err(err_to_string)?;
let _drop = std::io::copy(&mut reader, &mut writer);
std::fs::read_to_string(&file)
.map_err(err_to_string)
.map(|code| (filename.to_string(), code))
}
Err(e) => Err(format!("{e:?}")),
}
}
}
}
fn err_to_string<E: std::fmt::Debug>(e: E) -> String {
format!("{e:?}")
}
/// # Errors
/// # Panics
pub fn get_code(lib: &str) -> Result<(String, String), String> {
let url = url::Url::from_str(lib).map_err(err_to_string)?;
let segments = url.path_segments().ok_or_else(|| "lib url has no segments".to_string())?;
let filename = segments.last().ok_or_else(|| "lib url has no segments".to_string())?;
let mut file = PathBuf::from_str("target").map_err(err_to_string)?;
file.push(filename);
if let Ok(code) = std::fs::read_to_string(&file) {
println!("[{filename}] - using [{}]", file.display());
Ok((filename.to_string(), code))
} else {
println!("[{filename}] - Downloading [{lib}] to [{}]", file.display());
match ureq::get(lib).call() {
Ok(response) => {
let mut reader = response.into_reader();
let _drop = std::fs::remove_file(&file);
let mut writer = std::fs::File::create(&file).map_err(err_to_string)?;
let _drop = std::io::copy(&mut reader, &mut writer);
std::fs::read_to_string(&file)
.map_err(err_to_string)
.map(|code| (filename.to_string(), code))
}
Err(e) => Err(format!("{e:?}")),
}
}
}

View file

@ -1,5 +0,0 @@
https://cdn.jsdelivr.net/npm/pdfjs-dist@2.12.313/build/pdf.js
https://cdn.jsdelivr.net/npm/lodash@4.17.0/lodash.js
https://cdn.jsdelivr.net/npm/d3@7.1.1/dist/d3.js
https://cdn.jsdelivr.net/npm/typescript@4.6.2/lib/typescript.js
https://cdn.jsdelivr.net/npm/babylonjs@4.2.1/babylon.max.js

View file

@ -1,19 +1,5 @@
#![cfg(not(miri))] // Miri does not support custom allocators
use std::{
hint::black_box, // See: `https://rust-lang.github.io/rfcs/2360-bench-black-box.html`
rc::Rc,
time::Duration,
};
use criterion::{BenchmarkId, Criterion, Throughput};
use oxc_allocator::Allocator;
use oxc_ast::SourceType;
use oxc_benchmark::get_code;
use oxc_parser::Parser;
use oxc_semantic::SemanticBuilder;
use pico_args::Arguments;
//
#[cfg(not(target_env = "msvc"))]
#[global_allocator]
static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
@ -22,30 +8,49 @@ static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
use std::{
hint::black_box, // See: `https://rust-lang.github.io/rfcs/2360-bench-black-box.html`
rc::Rc,
};
use criterion::{BenchmarkId, Criterion, Throughput};
use oxc_allocator::Allocator;
use oxc_ast::SourceType;
use oxc_benchmark::Code;
use oxc_parser::Parser;
use oxc_semantic::SemanticBuilder;
use pico_args::Arguments;
/// # Errors
/// # Panics
pub fn main() -> Result<(), &'static str> {
pub fn main() -> Result<(), String> {
let codes = vec![
Code::new(5, "https://cdn.jsdelivr.net/npm/pdfjs-dist@2.12.313/build/pdf.js")?,
Code::new(5, "https://cdn.jsdelivr.net/npm/lodash@4.17.0/lodash.js")?,
Code::new(5, "https://cdn.jsdelivr.net/npm/d3@7.1.1/dist/d3.js")?,
Code::new(10, "https://cdn.jsdelivr.net/npm/typescript@4.6.2/lib/typescript.js")?,
Code::new(10, "https://cdn.jsdelivr.net/npm/babylonjs@4.2.1/babylon.max.js")?,
];
let mut args = Arguments::from_env();
let baseline: Option<String> = args.opt_value_from_str("--save-baseline").unwrap();
let mut criterion = Criterion::default().without_plots().measurement_time(Duration::new(20, 0));
let mut criterion = Criterion::default().without_plots();
if let Some(ref baseline) = baseline {
criterion = criterion.save_baseline(baseline.to_string());
}
let codes =
include_str!("./libs.txt").lines().map(|lib| get_code(lib).unwrap()).collect::<Vec<_>>();
// Check files
for (_, code) in &codes {
for code in &codes {
let allocator = Allocator::default();
let ret = Parser::new(&allocator, black_box(code), SourceType::default()).parse();
let ret =
Parser::new(&allocator, black_box(&code.source_text), SourceType::default()).parse();
if !ret.errors.is_empty() {
for error in &ret.errors {
println!("{error:?}");
}
return Err("Parse Failed.");
return Err("Parse Failed.".to_string());
}
}
@ -55,33 +60,46 @@ pub fn main() -> Result<(), &'static str> {
Ok(())
}
fn bench_parser(criterion: &mut Criterion, codes: &[(String, String)]) {
fn bench_parser(criterion: &mut Criterion, codes: &[Code]) {
let mut group = criterion.benchmark_group("parser");
for (id, code) in codes {
group.throughput(Throughput::Bytes(code.len() as u64));
group.bench_with_input(BenchmarkId::from_parameter(id), &code, |b, code| {
let allocator = Allocator::default();
b.iter(|| {
let _drop = Parser::new(&allocator, black_box(code), SourceType::default()).parse();
});
});
for code in codes {
group.throughput(Throughput::Bytes(code.source_text.len() as u64));
group.measurement_time(code.measurement_time);
group.bench_with_input(
BenchmarkId::from_parameter(&code.file_name),
&code.source_text,
|b, source_text| {
let allocator = Allocator::default();
b.iter(|| {
let _drop =
Parser::new(&allocator, black_box(source_text), SourceType::default())
.parse();
});
},
);
}
group.finish();
}
fn bench_semantic(criterion: &mut Criterion, codes: &[(String, String)]) {
fn bench_semantic(criterion: &mut Criterion, codes: &[Code]) {
let mut group = criterion.benchmark_group("semantic");
for (id, code) in codes {
group.throughput(Throughput::Bytes(code.len() as u64));
group.bench_with_input(BenchmarkId::from_parameter(id), &code, |b, code| {
let allocator = Allocator::default();
let ret = Parser::new(&allocator, black_box(code), SourceType::default()).parse();
let program = allocator.alloc(ret.program);
let trivias = Rc::new(ret.trivias);
b.iter(|| {
let _semantic = SemanticBuilder::new().build(black_box(program), trivias.clone());
});
});
for code in codes {
group.throughput(Throughput::Bytes(code.source_text.len() as u64));
group.measurement_time(code.measurement_time);
group.bench_with_input(
BenchmarkId::from_parameter(&code.file_name),
&code.source_text,
|b, source_text| {
let allocator = Allocator::default();
let ret = Parser::new(&allocator, source_text, SourceType::default()).parse();
let program = allocator.alloc(ret.program);
let trivias = Rc::new(ret.trivias);
b.iter(|| {
let _semantic =
SemanticBuilder::new().build(black_box(program), trivias.clone());
});
},
);
}
group.finish();
}