Skip to content

Commit 1117757

Browse files
authored
[backend-comparison] Upload benchmarks to server (#1381)
Uploading is enabled with already implemented --share argument of the burnbench command line tool. The burnbench binary passes the URL of the server and the auth token to the cargo bench process using the additional arguments --sharing-url and --sharing-token respectively. The persistence module then upload the results when a --sharing-url is provided. The URL is for now hardcoded. The endpoint is production when compiling in release mode and it is localhost otherwise.
1 parent 59c84a6 commit 1117757

File tree

12 files changed

+178
-41
lines changed

12 files changed

+178
-41
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend-comparison/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ strum = { workspace = true }
4242
strum_macros = { workspace = true }
4343

4444
[dev-dependencies]
45+
rstest = { workspace = true }
4546
serial_test = { workspace = true }
4647

4748
[[bench]]

backend-comparison/benches/binary.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ impl<B: Backend, const D: usize> Benchmark for BinaryBenchmark<B, D> {
3636
}
3737

3838
#[allow(dead_code)]
39-
fn bench<B: Backend>(device: &B::Device) {
39+
fn bench<B: Backend>(device: &B::Device, url: Option<&str>, token: Option<&str>) {
4040
let benchmark = BinaryBenchmark::<B, 3> {
4141
shape: [32, 512, 1024].into(),
4242
device: device.clone(),
4343
};
4444

45-
save::<B>(vec![run_benchmark(benchmark)], device).unwrap();
45+
save::<B>(vec![run_benchmark(benchmark)], device, url, token).unwrap();
4646
}
4747

4848
fn main() {

backend-comparison/benches/custom_gelu.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ fn erf_positive<B: Backend, const D: usize>(x: Tensor<B, D>) -> Tensor<B, D> {
114114
}
115115

116116
#[allow(dead_code)]
117-
fn bench<B: Backend>(device: &B::Device) {
117+
fn bench<B: Backend>(device: &B::Device, url: Option<&str>, token: Option<&str>) {
118118
const D: usize = 3;
119119
let shape: Shape<D> = [32, 512, 2048].into();
120120

@@ -145,6 +145,8 @@ fn bench<B: Backend>(device: &B::Device) {
145145
run_benchmark(custom_erf_gelu),
146146
],
147147
device,
148+
url,
149+
token,
148150
)
149151
.unwrap();
150152
};

backend-comparison/benches/data.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ impl<B: Backend, const D: usize> Benchmark for FromDataBenchmark<B, D> {
7171
}
7272

7373
#[allow(dead_code)]
74-
fn bench<B: Backend>(device: &B::Device) {
74+
fn bench<B: Backend>(device: &B::Device, url: Option<&str>, token: Option<&str>) {
7575
const D: usize = 3;
7676
let shape: Shape<D> = [32, 512, 1024].into();
7777

@@ -81,6 +81,8 @@ fn bench<B: Backend>(device: &B::Device) {
8181
save::<B>(
8282
vec![run_benchmark(to_benchmark), run_benchmark(from_benchmark)],
8383
device,
84+
url,
85+
token,
8486
)
8587
.unwrap();
8688
}

backend-comparison/benches/matmul.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ impl<B: Backend, const D: usize> Benchmark for MatmulBenchmark<B, D> {
4242
}
4343

4444
#[allow(dead_code)]
45-
fn bench<B: Backend>(device: &B::Device) {
45+
fn bench<B: Backend>(device: &B::Device, url: Option<&str>, token: Option<&str>) {
4646
const D: usize = 3;
4747
let batch_size = 3;
4848
let m = 1024;
@@ -53,7 +53,7 @@ fn bench<B: Backend>(device: &B::Device) {
5353

5454
let benchmark = MatmulBenchmark::<B, D>::new(shape_lhs, shape_rhs, device.clone());
5555

56-
save::<B>(vec![run_benchmark(benchmark)], device).unwrap();
56+
save::<B>(vec![run_benchmark(benchmark)], device, url, token).unwrap();
5757
}
5858

5959
fn main() {

backend-comparison/benches/unary.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ impl<B: Backend, const D: usize> Benchmark for UnaryBenchmark<B, D> {
3535
}
3636

3737
#[allow(dead_code)]
38-
fn bench<B: Backend>(device: &B::Device) {
38+
fn bench<B: Backend>(device: &B::Device, url: Option<&str>, token: Option<&str>) {
3939
const D: usize = 3;
4040
let shape: Shape<D> = [32, 512, 1024].into();
4141

4242
let benchmark = UnaryBenchmark::<B, D>::new(shape, device.clone());
4343

44-
save::<B>(vec![run_benchmark(benchmark)], device).unwrap();
44+
save::<B>(vec![run_benchmark(benchmark)], device, url, token).unwrap();
4545
}
4646

4747
fn main() {

backend-comparison/src/burnbenchapp/base.rs

+51-9
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,26 @@ use super::{
1616
};
1717

1818
const FIVE_SECONDS: time::Duration = time::Duration::new(5, 0);
19+
const USER_BENCHMARK_SERVER_URL: &str = if cfg!(debug_assertions) {
20+
// development
21+
"http://localhost:8000/benchmarks"
22+
} else {
23+
// production
24+
"https://user-benchmark-server-gvtbw64teq-nn.a.run.app/benchmarks"
25+
};
1926

2027
/// Base trait to define an application
2128
pub(crate) trait Application {
2229
fn init(&mut self) {}
2330

2431
#[allow(unused)]
25-
fn run(&mut self, benches: &[BenchmarkValues], backends: &[BackendValues]) {}
32+
fn run(
33+
&mut self,
34+
benches: &[BenchmarkValues],
35+
backends: &[BackendValues],
36+
token: Option<&str>,
37+
) {
38+
}
2639

2740
fn cleanup(&mut self) {}
2841
}
@@ -150,17 +163,17 @@ fn command_list() {
150163
}
151164

152165
fn command_run(run_args: RunArgs) {
166+
let token = get_token_from_cache();
153167
if run_args.share {
154168
// Verify if a token is saved
155-
let token = get_token_from_cache();
156169
if token.is_none() {
157170
eprintln!("You need to be authenticated to be able to share benchmark results.");
158171
eprintln!("Run the command 'burnbench auth' to authenticate.");
159172
return;
160173
}
161174
// TODO refresh the token when it is expired
162175
// Check for the validity of the saved token
163-
if !verify_token(&token.unwrap()) {
176+
if !verify_token(token.as_deref().unwrap()) {
164177
eprintln!("Your access token is no longer valid.");
165178
eprintln!("Run the command 'burnbench auth' again to get a new token.");
166179
return;
@@ -179,13 +192,12 @@ fn command_run(run_args: RunArgs) {
179192
let mut app = App::new();
180193
app.init();
181194
println!("Running benchmarks...");
182-
app.run(&run_args.benches, &run_args.backends);
195+
app.run(
196+
&run_args.benches,
197+
&run_args.backends,
198+
token.as_deref().filter(|_| run_args.share),
199+
);
183200
app.cleanup();
184-
println!("Cleanup completed. Benchmark run(s) finished.");
185-
if run_args.share {
186-
println!("Sharing results...");
187-
// TODO Post the results once backend can verify the GitHub access token
188-
}
189201
}
190202

191203
#[allow(unused)] // for tui as this is WIP
@@ -203,3 +215,33 @@ pub(crate) fn run_cargo(command: &str, params: &[&str]) {
203215
std::process::exit(status.code().unwrap_or(1));
204216
}
205217
}
218+
219+
pub(crate) fn run_backend_comparison_benchmarks(
220+
benches: &[BenchmarkValues],
221+
backends: &[BackendValues],
222+
token: Option<&str>,
223+
) {
224+
// Iterate over each combination of backend and bench
225+
for backend in backends.iter() {
226+
for bench in benches.iter() {
227+
let bench_str = bench.to_string();
228+
let backend_str = backend.to_string();
229+
let mut args = vec![
230+
"-p",
231+
"backend-comparison",
232+
"--bench",
233+
&bench_str,
234+
"--features",
235+
&backend_str,
236+
];
237+
if let Some(t) = token {
238+
args.push("--");
239+
args.push("--sharing-url");
240+
args.push(USER_BENCHMARK_SERVER_URL);
241+
args.push("--sharing-token");
242+
args.push(t);
243+
}
244+
run_cargo("bench", &args);
245+
}
246+
}
247+
}

backend-comparison/src/burnbenchapp/term/base.rs

+10-16
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::burnbenchapp::{run_cargo, Application, BackendValues, BenchmarkValues};
1+
use crate::burnbenchapp::{
2+
run_backend_comparison_benchmarks, Application, BackendValues, BenchmarkValues,
3+
};
24

35
use derive_new::new;
46

@@ -8,21 +10,13 @@ pub struct TermApplication;
810
impl Application for TermApplication {
911
fn init(&mut self) {}
1012

11-
fn run(&mut self, benches: &[BenchmarkValues], backends: &[BackendValues]) {
12-
// Iterate over each combination of backend and bench
13-
for backend in backends.iter() {
14-
for bench in benches.iter() {
15-
run_cargo(
16-
"bench",
17-
&[
18-
"--bench",
19-
&bench.to_string(),
20-
"--features",
21-
&backend.to_string(),
22-
],
23-
);
24-
}
25-
}
13+
fn run(
14+
&mut self,
15+
benches: &[BenchmarkValues],
16+
backends: &[BackendValues],
17+
token: Option<&str>,
18+
) {
19+
run_backend_comparison_benchmarks(benches, backends, token)
2620
}
2721

2822
fn cleanup(&mut self) {}

backend-comparison/src/burnbenchapp/tui/base.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@ impl Application for TuiApplication {
3232
fn init(&mut self) {}
3333

3434
#[allow(unused)]
35-
fn run(&mut self, benches: &[BenchmarkValues], backends: &[BackendValues]) {
35+
fn run(
36+
&mut self,
37+
benches: &[BenchmarkValues],
38+
backends: &[BackendValues],
39+
token: Option<&str>,
40+
) {
3641
// TODO initialize widgets given passed benches and backends on the command line
3742
loop {
3843
self.terminal

backend-comparison/src/lib.rs

+65-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,44 @@
11
pub mod burnbenchapp;
22
pub mod persistence;
33

4+
/// Simple parse to retrieve additional argument passed to cargo bench command
5+
/// We cannot use clap here as clap parser does not allow to have unknown arguments.
6+
pub fn get_argument<'a>(args: &'a [String], arg_name: &'a str) -> Option<&'a str> {
7+
let mut i = 0;
8+
while i < args.len() {
9+
match args[i].as_str() {
10+
arg if arg == arg_name && i + 1 < args.len() => {
11+
return Some(&args[i + 1]);
12+
}
13+
_ => i += 1,
14+
}
15+
}
16+
None
17+
}
18+
19+
/// Specialized function to retrieve the sharing token
20+
pub fn get_sharing_token(args: &[String]) -> Option<&str> {
21+
get_argument(args, "--sharing-token")
22+
}
23+
24+
/// Specialized function to retrieve the sharing URL
25+
pub fn get_sharing_url(args: &[String]) -> Option<&str> {
26+
get_argument(args, "--sharing-url")
27+
}
28+
429
#[macro_export]
530
macro_rules! bench_on_backend {
631
() => {
32+
use std::env;
33+
let args: Vec<String> = env::args().collect();
34+
let url = backend_comparison::get_sharing_url(&args);
35+
let token = backend_comparison::get_sharing_token(&args);
36+
737
#[cfg(feature = "wgpu")]
838
{
939
use burn::backend::wgpu::{AutoGraphicsApi, Wgpu, WgpuDevice};
1040

11-
bench::<Wgpu<AutoGraphicsApi, f32, i32>>(&WgpuDevice::default());
41+
bench::<Wgpu<AutoGraphicsApi, f32, i32>>(&WgpuDevice::default(), url, token);
1242
}
1343

1444
#[cfg(feature = "tch-gpu")]
@@ -19,15 +49,15 @@ macro_rules! bench_on_backend {
1949
let device = LibTorchDevice::Cuda(0);
2050
#[cfg(target_os = "macos")]
2151
let device = LibTorchDevice::Mps;
22-
bench::<LibTorch>(&device);
52+
bench::<LibTorch>(&device, url, token);
2353
}
2454

2555
#[cfg(feature = "tch-cpu")]
2656
{
2757
use burn::backend::{libtorch::LibTorchDevice, LibTorch};
2858

2959
let device = LibTorchDevice::Cpu;
30-
bench::<LibTorch>(&device);
60+
bench::<LibTorch>(&device, url, token);
3161
}
3262

3363
#[cfg(any(
@@ -41,7 +71,7 @@ macro_rules! bench_on_backend {
4171
use burn::backend::NdArray;
4272

4373
let device = NdArrayDevice::Cpu;
44-
bench::<NdArray>(&device);
74+
bench::<NdArray>(&device, url, token);
4575
}
4676

4777
#[cfg(feature = "candle-cpu")]
@@ -50,7 +80,7 @@ macro_rules! bench_on_backend {
5080
use burn::backend::Candle;
5181

5282
let device = CandleDevice::Cpu;
53-
bench::<Candle>(&device);
83+
bench::<Candle>(&device, url, token);
5484
}
5585

5686
#[cfg(feature = "candle-cuda")]
@@ -59,7 +89,7 @@ macro_rules! bench_on_backend {
5989
use burn::backend::Candle;
6090

6191
let device = CandleDevice::Cuda(0);
62-
bench::<Candle>(&device);
92+
bench::<Candle>(&device, url, token);
6393
}
6494

6595
#[cfg(feature = "candle-metal")]
@@ -68,7 +98,35 @@ macro_rules! bench_on_backend {
6898
use burn::backend::Candle;
6999

70100
let device = CandleDevice::Metal(0);
71-
bench::<Candle>(&device);
101+
bench::<Candle>(&device, url, token);
72102
}
73103
};
74104
}
105+
106+
#[cfg(test)]
107+
mod tests {
108+
use super::*;
109+
use rstest::rstest;
110+
111+
#[rstest]
112+
#[case::sharing_token_argument_with_value(&["--sharing-token", "token123"], Some("token123"))]
113+
#[case::sharing_token_argument_no_value(&["--sharing-token"], None)]
114+
#[case::sharing_token_argument_with_additional_arguments(&["--other-arg", "value", "--sharing-token", "token789"], Some("token789"))]
115+
#[case::other_argument(&["--other-arg", "value"], None)]
116+
#[case::no_argument(&[], None)]
117+
fn test_get_sharing_token(#[case] args: &[&str], #[case] expected: Option<&str>) {
118+
let args = args.iter().map(|s| s.to_string()).collect::<Vec<String>>();
119+
assert_eq!(get_sharing_token(&args), expected);
120+
}
121+
122+
#[rstest]
123+
#[case::sharing_url_argument_with_value(&["--sharing-url", "url123"], Some("url123"))]
124+
#[case::sharing_url_argument_no_value(&["--sharing-url"], None)]
125+
#[case::sharing_url_argument_with_additional_arguments(&["--other-arg", "value", "--sharing-url", "url789"], Some("url789"))]
126+
#[case::other_argument(&["--other-arg", "value"], None)]
127+
#[case::no_argument(&[], None)]
128+
fn test_get_sharing_url(#[case] args: &[&str], #[case] expected: Option<&str>) {
129+
let args = args.iter().map(|s| s.to_string()).collect::<Vec<String>>();
130+
assert_eq!(get_sharing_url(&args), expected);
131+
}
132+
}

0 commit comments

Comments
 (0)