From cb886d8a3688d25429896bded62329dd1374158e Mon Sep 17 00:00:00 2001 From: Boshen Date: Sun, 5 Mar 2023 23:19:51 +0800 Subject: [PATCH] refactor(benchmark): make the benchmark run faster by using variable measurement time --- tasks/benchmark/src/lib.rs | 90 +++++++++++++++++------------ tasks/benchmark/src/libs.txt | 5 -- tasks/benchmark/src/main.rs | 108 ++++++++++++++++++++--------------- 3 files changed, 118 insertions(+), 85 deletions(-) delete mode 100644 tasks/benchmark/src/libs.txt diff --git a/tasks/benchmark/src/lib.rs b/tasks/benchmark/src/lib.rs index 1e051048c..bc7f8bce7 100644 --- a/tasks/benchmark/src/lib.rs +++ b/tasks/benchmark/src/lib.rs @@ -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 { + 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: 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:?}")), - } - } -} diff --git a/tasks/benchmark/src/libs.txt b/tasks/benchmark/src/libs.txt deleted file mode 100644 index 82c28f323..000000000 --- a/tasks/benchmark/src/libs.txt +++ /dev/null @@ -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 diff --git a/tasks/benchmark/src/main.rs b/tasks/benchmark/src/main.rs index 7f84ddfb4..29195c12c 100644 --- a/tasks/benchmark/src/main.rs +++ b/tasks/benchmark/src/main.rs @@ -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 = 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::>(); - // 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(); }