1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
//! Benchmarks for expanding minified package metadata.
//!
//! The workload mirrors `ComposerRepository::load_async_packages`: every expanded version is
//! inspected, but only the ones accepted by the constraint and stability filters are kept. The
//! `keep` parameter is that acceptance rate, which is what decides how much of the expansion has
//! to be copied out.
use criterion::BatchSize;
use criterion::BenchmarkId;
use criterion::Criterion;
use criterion::criterion_group;
use criterion::criterion_main;
use indexmap::IndexMap;
use shirabe::json::json_file::JsonFile;
use shirabe_metadata_minifier::MetadataMinifier;
use shirabe_php_shim::PhpMixed;
use std::hint::black_box;
#[path = "packagist_fixture.rs"]
mod packagist_fixture;
/// Real packagist metadata, fetched on first run. `symfony/console` and `laravel/framework` are
/// long-lived packages whose p2 files carry hundreds of versions each.
const PACKAGES: &[&str] = &["monolog/monolog", "symfony/console", "laravel/framework"];
/// Percentages of the expanded versions the caller keeps.
const KEEP_PERCENTS: &[u32] = &[0, 10, 50, 100];
/// The minified version list for `package`, in the shape `load_async_packages` hands to the
/// minifier.
fn minified_versions(package: &str) -> Vec<IndexMap<String, PhpMixed>> {
let response = JsonFile::parse_json(Some(&packagist_fixture::fetch(package)), None)
.expect("invalid fixture JSON");
let response = response.as_array().expect("the fixture is not an object");
assert_eq!(
response.get("minified").and_then(|v| v.as_string()),
Some("composer/2.0"),
"{package} is not served in the minified format",
);
response
.get("packages")
.and_then(|v| v.as_array())
.and_then(|packages| packages.get(package))
.and_then(|v| v.as_list())
.expect("the fixture has no version list for the package")
.iter()
.map(|version| {
version
.as_array()
.expect("a version is not an object")
.clone()
})
.collect()
}
/// Reads one field of every expanded version and keeps the newest `keep` of them, the way the
/// repository keeps only the versions its filters accept.
fn consume(
versions: Vec<IndexMap<String, PhpMixed>>,
keep: usize,
) -> Vec<IndexMap<String, PhpMixed>> {
let expanded = MetadataMinifier::expand(versions);
let first_kept = expanded.len() - keep;
let mut kept: Vec<IndexMap<String, PhpMixed>> = Vec::with_capacity(keep);
for index in 0..expanded.len() {
black_box(expanded.version(index).get("version_normalized"));
if index >= first_kept {
kept.push(expanded.materialize(index));
}
}
kept
}
fn bench_expand(c: &mut Criterion) {
for package in PACKAGES {
let versions = minified_versions(package);
let mut group = c.benchmark_group(format!("expand/{package}"));
for keep_percent in KEEP_PERCENTS {
let keep = versions.len() * (*keep_percent as usize) / 100;
let parameter = format!("keep{keep_percent}%/{}of{}", keep, versions.len());
group.bench_function(BenchmarkId::from_parameter(parameter), |b| {
b.iter_batched(
|| versions.clone(),
|versions| consume(versions, keep),
BatchSize::PerIteration,
);
});
}
group.finish();
}
}
criterion_group!(benches, bench_expand);
criterion_main!(benches);
|