Rust benchmark: write native version

This commit is contained in:
Aaron Fenyes 2024-08-19 12:20:56 -07:00
parent 8ce3e251d7
commit eeb0f00534
4 changed files with 236 additions and 0 deletions

View File

@ -0,0 +1,3 @@
target/*
dist/*
Cargo.lock

View File

@ -0,0 +1,18 @@
[package]
name = "rust-benchmark-native"
version = "0.1.0"
authors = ["Aaron"]
edition = "2021"
[dependencies]
cairo-rs = "0.20.1"
gtk = { package = "gtk4", version = "0.9.0" }
##minifb = "0.27.0"
nalgebra = "0.33.0"
plotters = "0.3.6"
plotters-cairo = "0.7.0"
##plotters-gtk4 = { git = "https://github.com/baarkerlounger/plotters-gtk4.git", branch = "patch-1" }
[profile.release]
opt-level = "s" # optimize for small code size
debug = true # include debug symbols

View File

@ -0,0 +1,105 @@
use nalgebra::{*, allocator::Allocator};
use std::f64::consts::{PI, E};
/* dynamic matrices */
pub fn rand_eigval_series(dim: usize, time_res: usize) -> Vec<OVector<Complex<f64>, Dyn>> {
// initialize the random matrix
let mut rand_mat = DMatrix::<f64>::from_fn(dim, dim, |j, k| {
let n = j*dim + k;
E*((n*n) as f64) % 2.0 - 1.0
}) * (3.0 / (dim as f64)).sqrt();
// initialize the rotation step
let mut rot_step = DMatrix::<f64>::identity(dim, dim);
let max_freq = 4;
for n in (0..dim).step_by(2) {
let ang = PI * ((n % max_freq) as f64) / (time_res as f64);
let ang_cos = ang.cos();
let ang_sin = ang.sin();
rot_step[(n, n)] = ang_cos;
rot_step[(n+1, n)] = ang_sin;
rot_step[(n, n+1)] = -ang_sin;
rot_step[(n+1, n+1)] = ang_cos;
}
// find the eigenvalues
let mut eigval_series = Vec::<OVector<Complex<f64>, Dyn>>::with_capacity(time_res);
eigval_series.push(rand_mat.complex_eigenvalues());
for _ in 1..time_res {
rand_mat = &rot_step * rand_mat;
eigval_series.push(rand_mat.complex_eigenvalues());
}
eigval_series
}
/* dynamic single float matrices */
/*pub fn rand_eigval_series(dim: usize, time_res: usize) -> Vec<OVector<Complex<f32>, Dyn>> {
// initialize the random matrix
let mut rand_mat = DMatrix::<f32>::from_fn(dim, dim, |j, k| {
let n = j*dim + k;
(E as f32)*((n*n) as f32) % 2.0_f32 - 1.0_f32
}) * (3.0_f32 / (dim as f32)).sqrt();
// initialize the rotation step
let mut rot_step = DMatrix::<f32>::identity(dim, dim);
let max_freq = 4;
for n in (0..dim).step_by(2) {
let ang = (PI as f32) * ((n % max_freq) as f32) / (time_res as f32);
let ang_cos = ang.cos();
let ang_sin = ang.sin();
rot_step[(n, n)] = ang_cos;
rot_step[(n+1, n)] = ang_sin;
rot_step[(n, n+1)] = -ang_sin;
rot_step[(n+1, n+1)] = ang_cos;
}
// find the eigenvalues
let mut eigval_series = Vec::<OVector<Complex<f32>, Dyn>>::with_capacity(time_res);
eigval_series.push(rand_mat.complex_eigenvalues());
for _ in 1..time_res {
rand_mat = &rot_step * rand_mat;
eigval_series.push(rand_mat.complex_eigenvalues());
}
eigval_series
}*/
/* static matrices. should only be used when the dimension is really small */
/*pub fn rand_eigval_series<N>(time_res: usize) -> Vec<OVector<Complex<f64>, N>>
where
N: ToTypenum + DimName + DimSub<U1>,
DefaultAllocator:
Allocator<N> +
Allocator<N, N> +
Allocator<<N as DimSub<U1>>::Output> +
Allocator<N, <N as DimSub<U1>>::Output>
{
// initialize the random matrix
let dim = N::try_to_usize().unwrap();
let mut rand_mat = OMatrix::<f64, N, N>::from_fn(|j, k| {
let n = j*dim + k;
E*((n*n) as f64) % 2.0 - 1.0
}) * (3.0 / (dim as f64)).sqrt();
/*let mut rand_mat = OMatrix::<f64, N, N>::identity();*/
// initialize the rotation step
let mut rot_step = OMatrix::<f64, N, N>::identity();
let max_freq = 4;
for n in (0..dim).step_by(2) {
let ang = PI * ((n % max_freq) as f64) / (time_res as f64);
let ang_cos = ang.cos();
let ang_sin = ang.sin();
rot_step[(n, n)] = ang_cos;
rot_step[(n+1, n)] = ang_sin;
rot_step[(n, n+1)] = -ang_sin;
rot_step[(n+1, n+1)] = ang_cos;
}
// find the eigenvalues
let mut eigval_series = Vec::<OVector<Complex<f64>, N>>::with_capacity(time_res);
eigval_series.push(rand_mat.complex_eigenvalues());
for _ in 1..time_res {
rand_mat = &rot_step * rand_mat;
eigval_series.push(rand_mat.complex_eigenvalues());
}
eigval_series
}*/

View File

@ -0,0 +1,110 @@
// based on Olivier Pelhatre's GTK 3 example, ported to GTK 4
//
// https://github.com/Ouam74/RUST_Real-time_plots_using_GTK-rs_and_Plotters-rs
//
// a self-contained component might draw on the example below, by StackOverflow
// user Nicolas
//
// https://stackoverflow.com/a/76548487
//
// here's a crash course in `plotters`
//
// https://plotters-rs.github.io/book/basic/basic_data_plotting.html
//
extern crate cairo;
use plotters::prelude::*;
use plotters_cairo::CairoBackend;
use gtk::{
glib,
prelude::*,
Adjustment,
Align,
Application,
ApplicationWindow,
Box,
DrawingArea,
Label,
Orientation,
/*Picture,*/
Scale
};
/*use plotters_gtk4::Paintable;*/
use std::time::Instant;
mod engine;
fn main() -> glib::ExitCode {
let app = Application::builder()
.application_id("org.studioinfinity.rust-benchmark-native")
.build();
app.connect_activate(|app| {
/* run time is typically 77 ms, but at one point i was consistently
getting more like 260 ms. it's not clear what was causing that. building
from scratch seemed to give the short run time, and waiting a while
since the last build might also have had some effect */
const TIME_RES: usize = 100;
let start_time = Instant::now();
let eigval_series = engine::rand_eigval_series(60, TIME_RES);
let run_time = start_time.elapsed().as_millis();
// application state
let time_step = Adjustment::new(0.0, 0.0, TIME_RES as f64, 1.0, 0.0, 0.0);
// create the window.
let window = ApplicationWindow::builder()
.application(app)
.title("The circular law")
.build();
// create a vertical box
let container = Box::new(Orientation::Vertical, 5);
window.set_child(Some(&container));
// create the run time readout
let run_time_readout = Label::builder()
.margin_top(5)
.margin_start(10)
.halign(Align::Start)
.label(glib::gformat!("{} ms", run_time))
.build();
container.append(&run_time_readout);
// set up the drawing area
let drawing_area = DrawingArea::builder()
.content_width(600)
.content_height(600)
.build();
let time_step_for_draw = time_step.clone();
let draw_eigvals = move |_: &DrawingArea, context: &cairo::Context, width: i32, height: i32| {
let root = CairoBackend::new(&context, (width as u32, height as u32)).unwrap().into_drawing_area();
let _ = root.fill(&BLACK);
const R_DISP: f64 = 1.5;
let mut chart = ChartBuilder::on(&root)
.build_cartesian_2d(-R_DISP..R_DISP, -R_DISP..R_DISP)
.unwrap();
let time_step_val = (time_step_for_draw.value() as usize).min(TIME_RES-1);
let eigval_iter = eigval_series[time_step_val].iter();
let _ = chart.draw_series(
eigval_iter.map(|z| Circle::new((z.re, z.im), 3, WHITE.filled()))
);
let _ = root.present();
};
DrawingAreaExtManual::set_draw_func(&drawing_area, draw_eigvals);
container.append(&drawing_area);
// set up the time step slider
let time_step_scale = Scale::new(Orientation::Horizontal, Some(&time_step));
time_step_scale.connect_value_changed(move |_: &Scale| {
drawing_area.queue_draw();
});
container.append(&time_step_scale);
// show the window
window.present();
});
app.run()
}