use std::time::Instant;
use topos::{Backend, Shape, Tape, Tensor, init};
fn main() {
let mut accelerated = false;
for backend in Backend::ALL {
let status = backend.status();
accelerated |= status.is_ok();
match status {
Ok(()) => println!("{backend:?}: ready"),
Err(reason) => println!("{backend:?}: {reason}"),
}
}
let scale = if accelerated { 1 } else { 8 };
let batch_len = 1024 / scale;
let feature_len = 1024 / scale;
let hidden_len = 2048 / scale;
let class_len = 512 / scale;
let step_flops = (2 * batch_len * feature_len * hidden_len
+ 2 * batch_len * hidden_len * class_len) as f64
* 3.0;
let tape: Tape<f32> = Tape::new();
let mut initializer = init::kaiming(7);
let hidden_weights = tape.parameter(initializer(&Shape::new([feature_len, hidden_len])));
let output_weights = tape.parameter(initializer(&Shape::new([hidden_len, class_len])));
let features = tape.input(Tensor::filled([batch_len, feature_len], 0.0));
let targets = tape.input(Tensor::filled([batch_len, class_len], 0.0));
let hidden = features.matmul(hidden_weights).tanh();
let prediction = hidden.matmul(output_weights);
let error = prediction - targets;
let loss = (error * error).sum();
let (features, loss) = (features.symbol(), loss.symbol());
let network = tape.into_network();
let mut parameters = network.parameters();
let learning_rate = Tensor::new([], [0.0001_f32]);
let mut batch = init::normal(11, 1.0);
let batch = batch(&Shape::new([batch_len, feature_len]));
let side = 2048 / scale;
let left = initializer(&Shape::new([side, side]));
let right = initializer(&Shape::new([side, side]));
let product_flops = 2.0 * (side as f64).powi(3);
let rounds = if accelerated { 20 } else { 2 };
let started = Instant::now();
for _ in 0..rounds {
std::hint::black_box(left.matmul(&right));
}
let per_product = started.elapsed().as_secs_f64() / rounds as f64;
println!(
"raw product {side} x {side}: {:.1} GFLOP/s",
product_flops / per_product / 1e9
);
let warmup = 2;
let steps = 8;
let mut started = Instant::now();
for step in 0..warmup + steps {
if step == warmup {
started = Instant::now();
}
let run = network.forward(¶meters, [(features, batch.clone())]);
let gradients = run.backward(loss).parameters(¶meters);
parameters = parameters.step(&gradients, |parameter, gradient| {
parameter.clone() - gradient.clone() * learning_rate.broadcast_like(gradient)
});
}
let elapsed = started.elapsed().as_secs_f64();
let per_step = elapsed / steps as f64;
println!(
"training step, batch {batch_len}, {feature_len} -> {hidden_len} -> {class_len}: \
{per_step:.3} s/step, {:.1} GFLOP/s of matrix products",
step_flops / per_step / 1e9
);
}