use cubecl::{
benchmark::{Benchmark, ProfileDuration, TimingMethod},
client::Client,
future,
prelude::*,
std::tensor::TensorHandle,
zspace::Shape,
};
use cubek_test_utils::{RunSamples, TestInput};
use crate::{definition::InterpolateProblem, interpolate, interpolate_backward};
use crate::InterpolateStrategy;
pub fn bench(
strategy: &InterpolateStrategy,
problem: &InterpolateProblem,
num_samples: usize,
) -> Result<RunSamples, String> {
let device = cubecl::test_device();
let client = device.client();
let dtype = f32::elem_type_native();
let bench = InterpolateBench {
problem: problem.clone(),
strategy: *strategy,
device,
client: client.clone(),
dtype,
samples: num_samples,
};
let durations = bench
.run(cubek_test_utils::timing_method(TimingMethod::Device))
.map_err(|e| format!("benchmark failed: {e}"))?
.durations;
Ok(RunSamples::new(durations))
}
struct InterpolateBench {
problem: InterpolateProblem,
strategy: InterpolateStrategy,
device: cubecl::Device,
client: Client,
dtype: ElemType,
samples: usize,
}
impl Benchmark for InterpolateBench {
type Input = TensorHandle;
type Output = TensorHandle;
fn prepare(&self) -> Self::Input {
let shape = match &self.problem {
InterpolateProblem::Forward(prob) => prob.input_shape(),
InterpolateProblem::Backward(prob) => Shape::new(prob.out_grad_shape),
};
TestInput::builder(self.client.clone(), shape)
.dtype(self.dtype)
.uniform(0, -1., 1.)
.generate_without_host_data()
}
fn execute(&self, input: Self::Input) -> Result<TensorHandle, String> {
match &self.problem {
InterpolateProblem::Forward(prob) => {
let output = TensorHandle::empty(&self.client, prob.output_shape(), self.dtype);
interpolate(
&self.client,
input.binding(),
output.clone().binding(),
prob.options,
self.strategy,
self.dtype,
)
.map_err(|err| format!("{err}"))?;
Ok(output)
}
InterpolateProblem::Backward(prob) => {
let [n, _, _, c] = prob.out_grad_shape;
let input_grad_shape = vec![n, prob.input_size[0], prob.input_size[1], c];
let backward_input =
TestInput::builder(self.client.clone(), input_grad_shape.clone())
.dtype(self.dtype)
.uniform(0, -1., 1.)
.generate_without_host_data();
let input_grad = TensorHandle::empty(&self.client, input_grad_shape, self.dtype);
interpolate_backward(
&self.client,
backward_input.binding(),
input.clone().binding(),
input_grad.clone().binding(),
prob.options,
self.dtype,
)
.map_err(|err| format!("{err}"))?;
Ok(input_grad)
}
}
}
fn num_samples(&self) -> usize {
self.samples
}
fn name(&self) -> String {
match &self.problem {
InterpolateProblem::Forward(prob) => format!(
"interpolate-{:?}-{:?}-{:?}-{:?}-{:?}",
self.strategy,
self.dtype,
prob.options.mode,
self.device,
prob.input_shape(),
)
.to_lowercase(),
InterpolateProblem::Backward(prob) => format!(
"interpolate-backward-{:?}-{:?}-{:?}-{:?}-{:?}",
self.strategy, self.dtype, prob.options.mode, self.device, prob.out_grad_shape,
)
.to_lowercase(),
}
}
fn sync(&self) {
future::block_on(self.client.sync()).unwrap()
}
fn profile(&self, args: Self::Input) -> Result<ProfileDuration, String> {
cubek_test_utils::profile_launch(&self.client, "interpolate-bench", || self.execute(args))
}
}