Skip to content

Commit 4ec80c9

Browse files
committed
test(benches): add basic benchmark infrastructure
Refs: #599
1 parent ea45775 commit 4ec80c9

File tree

8 files changed

+174
-7
lines changed

8 files changed

+174
-7
lines changed

.cargo/config.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ linker = "rust-lld"
99

1010
[target.'cfg(target_env = "musl")']
1111
rustflags = ["-C", "target-feature=-crt-static"]
12+
13+
[profile.bench]
14+
debug = true

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ runtime = ["bindgen/runtime"]
5454
static = ["bindgen/static"]
5555

5656
[workspace]
57-
members = ["crates/macros", "crates/cli", "tests"]
57+
members = ["crates/macros", "crates/cli", "tests", "benches"]
5858

5959
[package.metadata.docs.rs]
6060
rustdoc-args = ["--cfg", "docs"]

benches/Cargo.toml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "benches"
3+
version = "0.1.0"
4+
edition = "2024"
5+
publish = false
6+
7+
[dependencies]
8+
ext-php-rs = { path = "../" }
9+
gungraun = { version = "0.17.0", features = ["client_requests"] }
10+
11+
[features]
12+
default = ["enum", "runtime", "closure"]
13+
enum = ["ext-php-rs/enum"]
14+
anyhow = ["ext-php-rs/anyhow"]
15+
runtime = ["ext-php-rs/runtime"]
16+
closure = ["ext-php-rs/closure"]
17+
static = ["ext-php-rs/static"]
18+
19+
[lib]
20+
crate-type = ["cdylib"]
21+
22+
[[bench]]
23+
name = "binary_bench"
24+
harness = false

benches/benches/binary_bench.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
use std::{
2+
process::Command,
3+
sync::{LazyLock, Once},
4+
};
5+
6+
use gungraun::{
7+
BinaryBenchmarkConfig, Callgrind, FlamegraphConfig, binary_benchmark, binary_benchmark_group,
8+
main,
9+
};
10+
11+
static BUILD: Once = Once::new();
12+
static EXT_TARGET_DIR: LazyLock<String> = LazyLock::new(|| {
13+
let mut dir = std::env::current_dir().expect("Could not get cwd");
14+
dir.pop();
15+
dir.push("target");
16+
dir.push("release");
17+
dir.display().to_string()
18+
});
19+
20+
fn setup() {
21+
BUILD.call_once(|| {
22+
let mut command = Command::new("cargo");
23+
command.arg("build");
24+
25+
command.arg("--release");
26+
27+
// Build features list dynamically based on compiled features
28+
// Note: Using vec_init_then_push pattern here is intentional due to conditional compilation
29+
#[allow(clippy::vec_init_then_push)]
30+
{
31+
let mut features = vec![];
32+
#[cfg(feature = "enum")]
33+
features.push("enum");
34+
#[cfg(feature = "closure")]
35+
features.push("closure");
36+
#[cfg(feature = "anyhow")]
37+
features.push("anyhow");
38+
#[cfg(feature = "runtime")]
39+
features.push("runtime");
40+
#[cfg(feature = "static")]
41+
features.push("static");
42+
43+
if !features.is_empty() {
44+
command.arg("--no-default-features");
45+
command.arg("--features").arg(features.join(","));
46+
}
47+
}
48+
49+
let result = command.output().expect("failed to execute cargo build");
50+
51+
assert!(
52+
result.status.success(),
53+
"Extension build failed:\nstdout: {}\nstderr: {}",
54+
String::from_utf8_lossy(&result.stdout),
55+
String::from_utf8_lossy(&result.stderr)
56+
);
57+
});
58+
}
59+
60+
#[binary_benchmark]
61+
#[bench::single_function_call(args = ("benches/function_call.php", 1))]
62+
#[bench::multiple_function_calls(args = ("benches/function_call.php", 10))]
63+
#[bench::lots_of_function_calls(args = ("benches/function_call.php", 100_000))]
64+
fn function_calls(path: &str, cnt: usize) -> gungraun::Command {
65+
setup();
66+
67+
gungraun::Command::new("php")
68+
.arg(format!("-dextension={}/libbenches.so", *EXT_TARGET_DIR))
69+
.arg(path)
70+
.arg(cnt.to_string())
71+
.build()
72+
}
73+
74+
binary_benchmark_group!(
75+
name = function;
76+
benchmarks = function_calls
77+
);
78+
79+
main!(
80+
config = BinaryBenchmarkConfig::default()
81+
.tool(Callgrind::with_args(["--instr-atstart=no", "--I1=32768,8,64", "--D1=32768,8,64", "--LL=67108864,16,64"])
82+
.flamegraph(FlamegraphConfig::default()));
83+
binary_benchmark_groups = function
84+
);

benches/benches/function_call.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
start_instrumentation();
6+
7+
foreach (range(1, $argv[1]) as $i) {
8+
bench_function($i);
9+
}
10+
11+
stop_instrumentation();

benches/src/lib.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#![cfg_attr(windows, feature(abi_vectorcall))]
2+
#![allow(
3+
clippy::must_use_candidate,
4+
clippy::missing_panics_doc,
5+
clippy::needless_pass_by_value,
6+
clippy::implicit_hasher
7+
)]
8+
9+
use ext_php_rs::{prelude::*, types::Zval};
10+
11+
#[php_function]
12+
pub fn bench_function(n: u64) -> u64 {
13+
// A simple function that does not do much work
14+
n
15+
}
16+
17+
#[php_function]
18+
pub fn bench_callback_function(callback: ZendCallable) -> Zval {
19+
// Call the provided PHP callable with a fixed argument
20+
callback
21+
.try_call(vec![&42])
22+
.expect("Failed to call function")
23+
}
24+
25+
#[php_function]
26+
pub fn start_instrumentation() {
27+
gungraun::client_requests::callgrind::start_instrumentation();
28+
// gungraun::client_requests::callgrind::toggle_collect();
29+
}
30+
31+
#[php_function]
32+
pub fn stop_instrumentation() {
33+
gungraun::client_requests::callgrind::stop_instrumentation();
34+
}
35+
36+
#[php_module]
37+
pub fn build_module(module: ModuleBuilder) -> ModuleBuilder {
38+
module
39+
.function(wrap_function!(bench_function))
40+
.function(wrap_function!(bench_callback_function))
41+
.function(wrap_function!(start_instrumentation))
42+
.function(wrap_function!(stop_instrumentation))
43+
}

flake.lock

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

flake.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@
2828
php-dev
2929
libclang.lib
3030
clang
31+
valgrind
3132
];
3233

3334
nativeBuildInputs = [ pkgs.rust-bin.stable.latest.default ];
3435

3536
shellHook = ''
3637
export LIBCLANG_PATH="${pkgs.libclang.lib}/lib"
38+
export GUNGRAUN_VALGRIND_INCLUDE="${pkgs.valgrind.dev}/include"
3739
'';
3840
};
3941
};

0 commit comments

Comments
 (0)