mirror of
https://github.com/uutils/grep.git
synced 2026-06-10 16:15:11 -07:00
Add grep benchmarks and CodSpeed CI workflow
Add a divan-based benchmark suite (benches/grep_bench.rs) modeled on the sed benchmarks, covering literal/fixed-string/regex search, case-insensitive matching, counting, inversion, line numbers, word/whole-line matching, and quiet mode. Wire up the dev-dependencies (codspeed-divan-compat, tempfile, uucore benchmark feature) and a [[bench]] entry. Add a Benchmarks GitHub Actions workflow running the suite through CodSpeed, adapted from the sed setup.
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
// Benchmarks for the grep utility
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// This file is part of the uutils grep package.
|
||||
// It is licensed under the MIT License.
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use divan::{Bencher, black_box};
|
||||
use uu_grep::uumain;
|
||||
use uucore::benchmark::{create_test_file, run_util_function};
|
||||
|
||||
/// Build an access-log-like data set with `n` lines.
|
||||
///
|
||||
/// Roughly a quarter of the lines use a non-default HTTP method / status /
|
||||
/// user-agent so that selective patterns match a realistic subset rather than
|
||||
/// every line or no line at all.
|
||||
fn access_log(n: usize) -> Vec<u8> {
|
||||
let mut data = Vec::new();
|
||||
for i in 0..n {
|
||||
let method = if i % 4 == 0 { "POST" } else { "GET" };
|
||||
let status = if i % 7 == 0 { 404 } else { 200 };
|
||||
let agent = if i % 3 == 0 {
|
||||
"Mozilla/5.0 (X11; Linux x86_64) Chrome/120.0"
|
||||
} else {
|
||||
"curl/8.5.0"
|
||||
};
|
||||
let line = format!(
|
||||
"192.168.{}.{} - - [01/Jan/2024:00:00:00 +0000] \"{} /index.html HTTP/1.1\" {} 1234 \"-\" \"{}\"\n",
|
||||
(i / 256) % 256,
|
||||
i % 256,
|
||||
method,
|
||||
status,
|
||||
agent,
|
||||
);
|
||||
data.extend_from_slice(line.as_bytes());
|
||||
}
|
||||
data
|
||||
}
|
||||
|
||||
/// Benchmark a literal search that matches nothing.
|
||||
///
|
||||
/// This is the purest measure of raw scan throughput: the whole file is read
|
||||
/// and searched but no output is produced.
|
||||
#[divan::bench]
|
||||
fn literal_no_match(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let file_path = create_test_file(&access_log(2_000_000), temp_dir.path());
|
||||
let file_path_str = file_path.to_str().unwrap();
|
||||
|
||||
bencher.bench(|| {
|
||||
black_box(run_util_function(
|
||||
uumain,
|
||||
&["ZZZ_NONEXISTENT_PATTERN_ZZZ", file_path_str],
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
/// Benchmark a literal search that matches a subset of lines.
|
||||
#[divan::bench]
|
||||
fn literal_match_some(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let file_path = create_test_file(&access_log(2_000_000), temp_dir.path());
|
||||
let file_path_str = file_path.to_str().unwrap();
|
||||
|
||||
bencher.bench(|| {
|
||||
black_box(run_util_function(uumain, &["POST", file_path_str]));
|
||||
});
|
||||
}
|
||||
|
||||
/// Benchmark a literal search that matches every line (counting only).
|
||||
///
|
||||
/// `-c` keeps the output bounded so the benchmark measures matching rather than
|
||||
/// terminal I/O.
|
||||
#[divan::bench]
|
||||
fn literal_match_all_count(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let file_path = create_test_file(&access_log(2_000_000), temp_dir.path());
|
||||
let file_path_str = file_path.to_str().unwrap();
|
||||
|
||||
bencher.bench(|| {
|
||||
black_box(run_util_function(uumain, &["-c", "HTTP", file_path_str]));
|
||||
});
|
||||
}
|
||||
|
||||
/// Benchmark a fixed-string search (`-F`).
|
||||
#[divan::bench]
|
||||
fn fixed_string(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let file_path = create_test_file(&access_log(2_000_000), temp_dir.path());
|
||||
let file_path_str = file_path.to_str().unwrap();
|
||||
|
||||
bencher.bench(|| {
|
||||
black_box(run_util_function(
|
||||
uumain,
|
||||
&["-F", "Chrome/120.0", file_path_str],
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
/// Benchmark a case-insensitive search (`-i`).
|
||||
#[divan::bench]
|
||||
fn case_insensitive(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let file_path = create_test_file(&access_log(2_000_000), temp_dir.path());
|
||||
let file_path_str = file_path.to_str().unwrap();
|
||||
|
||||
bencher.bench(|| {
|
||||
black_box(run_util_function(uumain, &["-i", "mozilla", file_path_str]));
|
||||
});
|
||||
}
|
||||
|
||||
/// Benchmark counting matches (`-c`).
|
||||
#[divan::bench]
|
||||
fn count(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let file_path = create_test_file(&access_log(2_000_000), temp_dir.path());
|
||||
let file_path_str = file_path.to_str().unwrap();
|
||||
|
||||
bencher.bench(|| {
|
||||
black_box(run_util_function(uumain, &["-c", "POST", file_path_str]));
|
||||
});
|
||||
}
|
||||
|
||||
/// Benchmark an inverted match (`-v`).
|
||||
///
|
||||
/// Most lines do not contain "POST", so this selects the majority of lines;
|
||||
/// `-c` bounds the output.
|
||||
#[divan::bench]
|
||||
fn invert_match_count(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let file_path = create_test_file(&access_log(2_000_000), temp_dir.path());
|
||||
let file_path_str = file_path.to_str().unwrap();
|
||||
|
||||
bencher.bench(|| {
|
||||
black_box(run_util_function(uumain, &["-vc", "POST", file_path_str]));
|
||||
});
|
||||
}
|
||||
|
||||
/// Benchmark printing line numbers (`-n`).
|
||||
#[divan::bench]
|
||||
fn line_number(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let file_path = create_test_file(&access_log(1_000_000), temp_dir.path());
|
||||
let file_path_str = file_path.to_str().unwrap();
|
||||
|
||||
bencher.bench(|| {
|
||||
black_box(run_util_function(uumain, &["-nc", "POST", file_path_str]));
|
||||
});
|
||||
}
|
||||
|
||||
/// Benchmark word-boundary matching (`-w`).
|
||||
#[divan::bench]
|
||||
fn word_match(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let file_path = create_test_file(&access_log(2_000_000), temp_dir.path());
|
||||
let file_path_str = file_path.to_str().unwrap();
|
||||
|
||||
bencher.bench(|| {
|
||||
black_box(run_util_function(uumain, &["-wc", "GET", file_path_str]));
|
||||
});
|
||||
}
|
||||
|
||||
/// Benchmark an extended regular expression with alternation (`-E`).
|
||||
#[divan::bench]
|
||||
fn extended_regex(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let file_path = create_test_file(&access_log(2_000_000), temp_dir.path());
|
||||
let file_path_str = file_path.to_str().unwrap();
|
||||
|
||||
bencher.bench(|| {
|
||||
black_box(run_util_function(
|
||||
uumain,
|
||||
&["-Ec", "(POST|DELETE|PUT)", file_path_str],
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
/// Benchmark a basic regular expression with an anchor and character class.
|
||||
#[divan::bench]
|
||||
fn basic_regex(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let file_path = create_test_file(&access_log(2_000_000), temp_dir.path());
|
||||
let file_path_str = file_path.to_str().unwrap();
|
||||
|
||||
bencher.bench(|| {
|
||||
black_box(run_util_function(
|
||||
uumain,
|
||||
&["-c", "^192\\.168\\.[0-9]*\\.0 ", file_path_str],
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
/// Benchmark a Perl-compatible regular expression (`-P`).
|
||||
#[divan::bench]
|
||||
fn perl_regex(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let file_path = create_test_file(&access_log(2_000_000), temp_dir.path());
|
||||
let file_path_str = file_path.to_str().unwrap();
|
||||
|
||||
bencher.bench(|| {
|
||||
black_box(run_util_function(
|
||||
uumain,
|
||||
&["-Pc", "\"\\d{3}\" \\d+", file_path_str],
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
/// Benchmark `--only-matching` (`-o`) extracting a substring from each line.
|
||||
#[divan::bench]
|
||||
fn only_matching(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let file_path = create_test_file(&access_log(1_000_000), temp_dir.path());
|
||||
let file_path_str = file_path.to_str().unwrap();
|
||||
|
||||
bencher.bench(|| {
|
||||
black_box(run_util_function(
|
||||
uumain,
|
||||
&["-Eoc", "[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+", file_path_str],
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
/// Benchmark quiet mode (`-q`), which can stop at the first match.
|
||||
#[divan::bench]
|
||||
fn quiet_first_match(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let file_path = create_test_file(&access_log(2_000_000), temp_dir.path());
|
||||
let file_path_str = file_path.to_str().unwrap();
|
||||
|
||||
bencher.bench(|| {
|
||||
black_box(run_util_function(uumain, &["-q", "POST", file_path_str]));
|
||||
});
|
||||
}
|
||||
|
||||
/// Benchmark searching short numeric lines (many small lines).
|
||||
#[divan::bench]
|
||||
fn short_lines(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let mut data = Vec::new();
|
||||
for i in 0..10_000_000 {
|
||||
data.extend_from_slice(format!("{i}\n").as_bytes());
|
||||
}
|
||||
let file_path = create_test_file(&data, temp_dir.path());
|
||||
let file_path_str = file_path.to_str().unwrap();
|
||||
|
||||
bencher.bench(|| {
|
||||
black_box(run_util_function(uumain, &["-c", "999", file_path_str]));
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {
|
||||
divan::main();
|
||||
}
|
||||
Reference in New Issue
Block a user