Skip to content

Commit 087ccf0

Browse files
committed
feat(hostname): add benchmark with large /etc/hosts
Adds comprehensive benchmarks for the hostname utility, including: - Realistic hosts file generation with various hostname patterns - Benchmarks for all flags: -s, -d, -f, -i - Parameterized tests with 1K, 10K, 100K entries - NSS_WRAPPER support for testing with generated hosts files - CI integration for automated benchmark tracking Fixes #10949
1 parent 2993148 commit 087ccf0

File tree

4 files changed

+147
-0
lines changed

4 files changed

+147
-0
lines changed

.github/workflows/benchmarks.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ jobs:
3737
uu_du,
3838
uu_expand,
3939
uu_fold,
40+
uu_hostname,
4041
uu_join,
4142
uu_ls,
4243
uu_mv,

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/uu/hostname/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,12 @@ path = "src/main.rs"
3939

4040
[package.metadata.cargo-udeps.ignore]
4141
normal = ["uucore_procs"]
42+
43+
[[bench]]
44+
name = "hostname_bench"
45+
harness = false
46+
47+
[dev-dependencies]
48+
divan = { workspace = true }
49+
tempfile = { workspace = true }
50+
uucore = { workspace = true, features = ["benchmark"] }
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// This file is part of the uutils coreutils package.
2+
//
3+
// For the full copyright and license information, please view the LICENSE
4+
// file that was distributed with this source code.
5+
6+
//! Benchmarks for hostname utility
7+
//!
8+
//! This benchmark tests the performance of hostname with large /etc/hosts files,
9+
//! specifically targeting the DNS resolution functionality (`-i` flag).
10+
//!
11+
//! # Important Note on Large Hosts File Testing
12+
//!
13+
//! To properly test with large /etc/hosts files, NSS_WRAPPER library must be used:
14+
//!
15+
//! ```bash
16+
//! LD_PRELOAD=/usr/lib/libnss_wrapper.so NSS_WRAPPER_HOSTS=/tmp/large_hosts \
17+
//! cargo bench --package uu_hostname hostname_ip_lookup
18+
//! ```
19+
//!
20+
//! Without NSS_WRAPPER, the benchmark tests with the system's real /etc/hosts.
21+
22+
use divan::{Bencher, black_box};
23+
use std::io::Write;
24+
use uu_hostname::uumain;
25+
use uucore::benchmark::run_util_function;
26+
27+
/// Generate a large hosts file with the specified number of entries
28+
fn generate_hosts_file(entries: usize) -> Vec<u8> {
29+
let avg_line_size = 80;
30+
let mut data = Vec::with_capacity(entries * avg_line_size);
31+
let mut writer = std::io::BufWriter::new(&mut data);
32+
33+
// Localhost entries
34+
writeln!(writer, "127.0.0.1 localhost localhost.localdomain").unwrap();
35+
writeln!(writer, "::1 localhost localhost.localdomain").unwrap();
36+
37+
// Generate host entries
38+
let environments = ["prod", "staging", "dev", "test"];
39+
let regions = ["us-east-1", "us-west-2", "eu-west-1", "ap-southeast-1"];
40+
let roles = [
41+
"web", "db", "cache", "api", "worker", "lb", "monitor", "backup",
42+
];
43+
44+
for i in 2..=entries {
45+
let ip = match i % 5 {
46+
0 => format!("127.0.0.{}", i % 256),
47+
1 => format!("10.{}.{}.{}", (i / 256) % 256, (i / 16) % 16, i % 256),
48+
2 => format!("192.168.{}.{}", (i / 256) % 256, i % 256),
49+
3 => format!("172.16.{}.{}", (i / 256) % 256, i % 256),
50+
_ => format!("10.0.{}.{}", (i / 256) % 256, i % 256),
51+
};
52+
53+
let role = roles[i % roles.len()];
54+
let env = environments[i % environments.len()];
55+
let region = regions[i % regions.len()];
56+
let fqdn = format!("{role}-{i:03}.{region}.{env}.example.com");
57+
let short = format!("{role}-{i:03}");
58+
59+
writeln!(writer, "{ip:<15} {fqdn} {short}").unwrap();
60+
}
61+
62+
writer.flush().unwrap();
63+
drop(writer);
64+
data
65+
}
66+
67+
/// Benchmark hostname -i with large hosts file
68+
///
69+
/// # Important
70+
/// Use NSS_WRAPPER to test with generated hosts file:
71+
/// `LD_PRELOAD=/usr/lib/libnss_wrapper.so NSS_WRAPPER_HOSTS=/path/to/hosts cargo bench`
72+
#[divan::bench(
73+
args = [100_000],
74+
name = "hostname_ip_lookup"
75+
)]
76+
fn bench_hostname_ip(bencher: Bencher, entries: usize) {
77+
let temp_dir = tempfile::tempdir().unwrap();
78+
let hosts_file = temp_dir.path().join("hosts");
79+
let hosts_data = generate_hosts_file(entries);
80+
std::fs::write(&hosts_file, &hosts_data).unwrap();
81+
82+
let nss_wrapper_env = std::env::var("LD_PRELOAD").unwrap_or_default();
83+
let has_nss_wrapper = nss_wrapper_env.contains("nss_wrapper");
84+
85+
if has_nss_wrapper {
86+
unsafe {
87+
std::env::set_var("NSS_WRAPPER_HOSTS", &hosts_file);
88+
}
89+
}
90+
91+
bencher.bench(|| {
92+
let result = black_box(run_util_function(uumain, &["-i"]));
93+
assert_eq!(result, 0);
94+
});
95+
96+
if has_nss_wrapper {
97+
unsafe {
98+
std::env::remove_var("NSS_WRAPPER_HOSTS");
99+
}
100+
}
101+
}
102+
103+
/// Benchmark basic hostname display (baseline)
104+
#[divan::bench(name = "hostname_basic")]
105+
fn bench_hostname_basic(bencher: Bencher) {
106+
bencher.bench(|| {
107+
let result = black_box(run_util_function(uumain, &[]));
108+
assert_eq!(result, 0);
109+
});
110+
}
111+
112+
/// Benchmark direct DNS lookup (Linux/macOS path)
113+
#[cfg(not(any(target_os = "freebsd", target_os = "openbsd")))]
114+
#[divan::bench(
115+
args = [100_000],
116+
name = "socket_addrs_direct"
117+
)]
118+
fn bench_socket_addrs_direct(bencher: Bencher, _entries: usize) {
119+
use std::net::ToSocketAddrs;
120+
121+
let hostname = hostname::get()
122+
.unwrap_or_default()
123+
.to_string_lossy()
124+
.into_owned();
125+
let hostname_with_port = format!("{hostname}:1");
126+
127+
bencher.bench(|| {
128+
let result: Result<Vec<_>, _> = hostname_with_port.to_socket_addrs().map(Iterator::collect);
129+
let _ = black_box(result);
130+
});
131+
}
132+
133+
fn main() {
134+
divan::main();
135+
}

0 commit comments

Comments
 (0)