diff --git a/examples/demo/src/fs.rs b/examples/demo/src/fs.rs new file mode 100644 index 000000000..03b4b8f8c --- /dev/null +++ b/examples/demo/src/fs.rs @@ -0,0 +1,65 @@ +use std::path::Path; +use std::{fs, io}; + +pub fn read_dir(path: &str) -> io::Result<()> { + eprintln!(); + + assert!(Path::new(path).is_dir()); + eprintln!("Reading {path:?} directory entries"); + let entries = fs::read_dir(path)?.collect::, _>>()?; + + assert!(!entries.is_empty()); + for entry in entries { + let path = entry.path(); + eprintln!("Found {path:?}"); + } + + Ok(()) +} + +pub fn read_version() -> io::Result<()> { + eprintln!(); + let path = "/proc/version"; + + eprint!("{path} contains"); + let version = fs::read_to_string(path).unwrap(); + eprintln!(" {version:?}"); + + Ok(()) +} + +fn test_file(path: &str) -> io::Result<()> { + let contents = "Hello, world!"; + + eprint!("{path:15} : writing"); + fs::write(path, contents)?; + + eprint!(", reading"); + let read = fs::read_to_string(path)?; + assert_eq!(contents, read); + + // FIXME: this returns an error on Uhyve + // eprintln!(", deleting"); + // fs::remove_file(path)?; + + Ok(()) +} + +pub fn file() -> io::Result<()> { + eprintln!(); + test_file("/tmp/hello.txt")?; + if cfg!(target_os = "hermit") && cfg!(feature = "fs") { + test_file("/root/hello.txt")?; + } + Ok(()) +} + +pub fn fs() -> io::Result<()> { + // FIXME: https://github.com/rust-lang/rust/pull/115984 + if cfg!(not(target_os = "hermit")) { + read_dir("/proc")?; + } + read_version()?; + file()?; + Ok(()) +} diff --git a/examples/demo/src/laplace.rs b/examples/demo/src/laplace.rs index 6a8e3e4bb..a84a4f5d3 100644 --- a/examples/demo/src/laplace.rs +++ b/examples/demo/src/laplace.rs @@ -1,30 +1,22 @@ use std::time::Instant; -use std::{thread, vec}; +use std::vec; use rayon::prelude::*; -pub fn laplace(size_x: usize, size_y: usize) -> Result<(), ()> { - let ncpus = thread::available_parallelism().unwrap().get(); - let pool = rayon::ThreadPoolBuilder::new() - .num_threads(ncpus) - .build() - .unwrap(); - let matrix = matrix_setup(size_x, size_y); +const SIZE: usize = if cfg!(debug_assertions) { 16 } else { 64 }; +pub fn laplace() { + eprintln!(); + + let matrix = matrix_setup(SIZE, SIZE); + + eprintln!("Laplace iterations"); let now = Instant::now(); - let (iterations, res) = pool.install(|| compute(matrix, size_x, size_y)); - println!( - "Time to solve {} s, iterations {}, residuum {}", - now.elapsed().as_secs_f64(), - iterations, - res - ); - - if res < 0.01 { - Ok(()) - } else { - Err(()) - } + let (i, residual) = compute(matrix, SIZE, SIZE); + let elapsed = now.elapsed(); + eprintln!("{i} iterations: {elapsed:?} (residual: {residual})"); + + assert!(residual < 0.001); } fn matrix_setup(size_x: usize, size_y: usize) -> vec::Vec> { @@ -97,7 +89,6 @@ fn iteration(cur: &[f64], next: &mut [f64], size_x: usize, size_y: usize) { }); } -#[inline(never)] pub fn compute(mut matrix: vec::Vec>, size_x: usize, size_y: usize) -> (usize, f64) { let mut counter = 0; diff --git a/examples/demo/src/main.rs b/examples/demo/src/main.rs index fb99df8a7..0f026a080 100644 --- a/examples/demo/src/main.rs +++ b/examples/demo/src/main.rs @@ -1,90 +1,60 @@ +use std::{env, f64, hint, io}; + #[cfg(target_os = "hermit")] use hermit as _; +mod fs; mod laplace; mod matmul; -mod tests; -mod thread_local; +mod pi; +mod thread; + +fn main() -> io::Result<()> { + hello(); + print_env(); + arithmetic(); + thread::sleep(); + thread::spawn()?; + fs::fs()?; + pi::pi(); + matmul::matmul(); + laplace::laplace(); + Ok(()) +} + +pub fn hello() { + eprintln!(); + eprintln!("Hello, Hermit! 🦀"); + eprintln!("Hello, world!"); + eprintln!("Привет, мир!"); + eprintln!("こんにちは世界!"); + eprintln!("你好世界!"); + eprintln!("สวัสดีชาวโลก!"); + eprintln!("Chào thế giới!"); +} -use tests::*; +pub fn print_env() { + eprintln!(); + eprintln!("Arguments:"); + for argument in env::args() { + eprintln!("{argument}"); + } -fn test_result(result: Result<(), T>) -> &'static str { - match result { - Ok(_) => "ok", - Err(_) => "failed!", + eprintln!(); + eprintln!("Environment variables:"); + for (key, value) in env::vars() { + eprintln!("{key}: {value}"); } } -fn main() { - println!("Test {} ... {}", stringify!(hello), test_result(hello())); - println!( - "Test {} ... {}", - stringify!(sleep), - test_result(test_sleep()) - ); - println!( - "Test {} ... {}", - stringify!(test_thread_local), - test_result(thread_local::test_thread_local()) - ); - println!( - "Test {} ... {}", - stringify!(arithmetic), - test_result(arithmetic()) - ); - println!( - "Test {} ... {}", - stringify!(print_argv), - test_result(print_argv()) - ); - println!( - "Test {} ... {}", - stringify!(print_env), - test_result(print_env()) - ); - println!( - "Test {} ... {}", - stringify!(read_file), - test_result(read_file()) - ); - println!( - "Test {} ... {}", - stringify!(read_dir), - test_result(read_dir()) - ); - println!( - "Test {} ... {}", - stringify!(create_file), - test_result(create_file()) - ); - println!( - "Test {} ... {}", - stringify!(threading), - test_result(threading()) - ); - println!( - "Test {} ... {}", - stringify!(pi_sequential), - test_result(pi_sequential(5000000)) - ); - println!( - "Test {} ... {}", - stringify!(pi_parallel), - test_result(pi_parallel(5000000)) - ); - println!( - "Test {} ... {}", - stringify!(laplace), - test_result(laplace::laplace(128, 128)) - ); - println!( - "Test {} ... {}", - stringify!(test_matmul_strassen), - test_result(matmul::test_matmul_strassen()) - ); - println!( - "Test {} ... {}", - stringify!(thread_creation), - test_result(thread_creation()) - ); +pub fn arithmetic() { + eprintln!(); + + let x = hint::black_box(f64::consts::PI) * 2.0; + let y: f64 = hint::black_box(x).exp(); + let z: f64 = hint::black_box(y).ln(); + + eprintln!("x = {x}"); + eprintln!("e^x = {y}"); + eprintln!("ln(e^x) = {z}"); } diff --git a/examples/demo/src/matmul.rs b/examples/demo/src/matmul.rs index 20a227e44..31f06170a 100644 --- a/examples/demo/src/matmul.rs +++ b/examples/demo/src/matmul.rs @@ -372,7 +372,7 @@ fn timed_matmul(size: usize, f: F, name: f(&a[..], &b[..], &mut dest[..]); let dur = Instant::now() - start; let nanos = u64::from(dur.subsec_nanos()) + dur.as_secs() * 1_000_000_000u64; - println!( + eprintln!( "{}:\t{}x{} matrix: {} s", name, size, @@ -384,7 +384,9 @@ fn timed_matmul(size: usize, f: F, name: const SIZE: usize = if cfg!(debug_assertions) { 64 } else { 256 }; -pub fn test_matmul_strassen() -> Result<(), ()> { +pub fn matmul() { + eprintln!(); + eprintln!("Matrix multiplication"); if SIZE <= 1024 { // Crappy algorithm takes several minutes on larger inputs. timed_matmul(SIZE, seq_matmul, "seq row-major"); @@ -397,7 +399,5 @@ pub fn test_matmul_strassen() -> Result<(), ()> { let par = timed_matmul(SIZE, matmulz, "par z-order"); timed_matmul(SIZE, matmul_strassen, "par strassen"); let speedup = seq as f64 / par as f64; - println!("speedup: {:.2}x", speedup); - - Ok(()) + eprintln!("speedup: {:.2}x", speedup); } diff --git a/examples/demo/src/pi.rs b/examples/demo/src/pi.rs new file mode 100644 index 000000000..f927d73de --- /dev/null +++ b/examples/demo/src/pi.rs @@ -0,0 +1,46 @@ +use std::time::Instant; +use std::{f64, hint}; + +use rayon::prelude::*; + +const STEPS: u64 = if cfg!(debug_assertions) { + 50_000 +} else { + 5_000_000 +}; +const STEP_SIZE: f64 = 1.0 / STEPS as f64; + +#[derive(Debug)] +pub enum Mode { + Sequential, + Parallel, +} + +fn calculate_pi(mode: Mode) { + eprintln!(); + eprint!("Calculating Pi {:14}", format!("({mode:?}): ")); + + let steps = hint::black_box(STEPS); + let map_step = |i| { + let x = (i as f64 + 0.5) * STEP_SIZE; + 4.0 / (1.0 + x * x) + }; + + let now = Instant::now(); + let sum = match mode { + Mode::Sequential => (0..steps).map(map_step).sum::(), + Mode::Parallel => (0..steps).into_par_iter().map(map_step).sum::(), + }; + let mypi = sum * STEP_SIZE; + let elapsed = now.elapsed(); + + eprintln!("{elapsed:?}"); + + assert!((mypi - f64::consts::PI).abs() < 1e-10); +} + +pub fn pi() { + eprintln!(); + calculate_pi(Mode::Sequential); + calculate_pi(Mode::Parallel); +} diff --git a/examples/demo/src/tests.rs b/examples/demo/src/tests.rs deleted file mode 100644 index 22c2bda83..000000000 --- a/examples/demo/src/tests.rs +++ /dev/null @@ -1,206 +0,0 @@ -use std::fs::File; -use std::io::{Read, Write}; -use std::time::{self, Instant}; -use std::{env, f64, hint, thread, vec}; - -use rayon::prelude::*; - -pub fn test_sleep() -> Result<(), ()> { - let one_sec = time::Duration::from_millis(1000); - let now = time::Instant::now(); - - thread::sleep(one_sec); - - let elapsed = now.elapsed().as_millis(); - println!("Measured time for 1 second sleep: {} ms", elapsed); - - if !(985..=1015).contains(&elapsed) { - Err(()) - } else { - Ok(()) - } -} - -pub fn thread_creation() -> Result<(), ()> { - const N: usize = 10; - - // cache warmup - { - let builder = thread::Builder::new(); - let child = builder.spawn(|| {}).unwrap(); - let _ = child.join(); - } - - let now = Instant::now(); - for _ in 0..N { - let builder = thread::Builder::new(); - let child = builder.spawn(|| {}).unwrap(); - let _ = child.join(); - } - - println!( - "Time to create and to join a thread: {} ms", - now.elapsed().as_secs_f64() * 1000.0f64 / f64::from(N as i32) - ); - - Ok(()) -} - -pub fn pi_sequential(num_steps: u64) -> Result<(), ()> { - let step = 1.0 / num_steps as f64; - let mut sum = 0 as f64; - - for i in 0..num_steps { - let x = (i as f64 + 0.5) * step; - sum += 4.0 / (1.0 + x * x); - } - - let mypi = sum * (1.0 / num_steps as f64); - println!("Pi: {mypi} (sequential)"); - - if (mypi - f64::consts::PI).abs() < 0.00001 { - Ok(()) - } else { - Err(()) - } -} - -pub fn pi_parallel(num_steps: u64) -> Result<(), ()> { - let ncpus = thread::available_parallelism().unwrap().get(); - let pool = rayon::ThreadPoolBuilder::new() - .num_threads(ncpus) - .build() - .unwrap(); - let step = 1.0 / num_steps as f64; - - let sum: f64 = pool.install(|| { - (0..num_steps) - .into_par_iter() - .map(|i| { - let x = (i as f64 + 0.5) * step; - 4.0 / (1.0 + x * x) - }) - .sum() - }); - - let mypi = sum * (1.0 / num_steps as f64); - println!("Pi: {mypi} (with {ncpus} threads)"); - - if (mypi - f64::consts::PI).abs() < 0.00001 { - Ok(()) - } else { - Err(()) - } -} - -pub fn read_file() -> Result<(), std::io::Error> { - let mut file = File::open("/proc/version")?; - let mut contents = String::new(); - file.read_to_string(&mut contents)?; - - println!("Version: {contents}"); - - Ok(()) -} - -pub fn read_dir() -> Result<(), std::io::Error> { - for entry in std::fs::read_dir("/proc")? { - let entry = entry?; - println!("Found {:?} in /proc", entry.file_name()); - } - - Ok(()) -} - -pub fn create_file() -> Result<(), std::io::Error> { - { - let mut file = File::create("/tmp/foo.txt")?; - file.write_all(b"Hello, world!")?; - } - - let content = { - let mut file = File::open("/tmp/foo.txt")?; - let mut content = String::new(); - file.read_to_string(&mut content)?; - content - }; - - // delete temporary file - std::fs::remove_file("/tmp/foo.txt")?; - - if content == "Hello, world!" { - Ok(()) - } else { - println!("Read invalid content: {} (len {})", content, content.len()); - let kind = std::io::ErrorKind::Other; - Err(std::io::Error::from(kind)) - } -} - -pub fn print_argv() -> Result<(), ()> { - let args = env::args(); - - // Prints each argument on a separate line - for (i, argument) in args.enumerate() { - println!("argument[{i}] = {argument}"); - } - - Ok(()) -} - -pub fn print_env() -> Result<(), ()> { - let envs = env::vars(); - - // We will iterate through the references to the element returned by - // env::vars(); - for (key, value) in envs { - println!("{key}: {value}"); - } - - Ok(()) -} - -pub fn hello() -> Result<(), ()> { - println!("Hello, world!"); - println!("Привет, мир!"); - println!("こんにちは世界!"); - println!("你好世界!"); - println!("สวัสดีชาวโลก!"); - println!("Chào thế giới!"); - let crab = vec![0xF0_u8, 0x9F_u8, 0xA6_u8, 0x80_u8]; - println!( - "Crab emoji: {}", - String::from_utf8(crab).unwrap_or_default() - ); - - Ok(()) -} - -pub fn arithmetic() -> Result<(), ()> { - let x = hint::black_box(f64::consts::PI) * 2.0; - let y: f64 = x.exp(); - let z: f64 = y.ln(); - - println!("x = {x}, e^x = {y}, ln(e^x) = {z}"); - - Ok(()) -} - -pub fn threading() -> Result<(), ()> { - // Make a vector to hold the children which are spawned. - let mut children = vec![]; - - for i in 0..2 { - // Spin up another thread - children.push(thread::spawn(move || { - println!("this is thread number {i}"); - })); - } - - for child in children { - // Wait for the thread to finish. Returns a result. - let _ = child.join(); - } - - Ok(()) -} diff --git a/examples/demo/src/thread.rs b/examples/demo/src/thread.rs new file mode 100644 index 000000000..dadbc292e --- /dev/null +++ b/examples/demo/src/thread.rs @@ -0,0 +1,63 @@ +use std::cell::Cell; +use std::time::{Duration, Instant}; +use std::{io, thread}; + +pub fn sleep() { + eprintln!(); + let duration = Duration::from_millis(100); + + let now = Instant::now(); + thread::sleep(duration); + let elapsed = now.elapsed(); + + eprintln!("Measured time for {duration:?} sleep: {elapsed:?}"); + assert!(elapsed >= duration); + let expected_delay = if cfg!(debug_assertions) { + Duration::from_millis(15) + } else { + Duration::from_millis(5) + }; + assert!(elapsed <= duration + expected_delay); +} + +pub fn spawn() -> io::Result<()> { + eprintln!(); + + let available_parallelism = thread::available_parallelism()?; + eprintln!("available_parallelism = {available_parallelism}"); + eprint!("Thread:"); + + let thread_number = available_parallelism.get() * 2; + let handlers = (0..thread_number) + .map(|i| { + thread::spawn(move || { + #[derive(Default)] + #[repr(align(0x10))] + struct Aligned(u8); + + thread_local! { + static THREAD_LOCAL: Cell = const { Cell::new(Aligned(0x42)) }; + } + + eprint!(" {i}"); + thread::sleep(Duration::from_millis( + u64::try_from(thread_number - i).unwrap() * 10, + )); + THREAD_LOCAL.with(|thread_local| { + assert_eq!(0x42, thread_local.take().0); + }); + thread::sleep(Duration::from_millis(u64::try_from(i).unwrap() * 20)); + THREAD_LOCAL.with(|thread_local| { + assert_eq!(0, thread_local.take().0); + }); + }) + }) + .collect::>(); + + for handler in handlers { + handler.join().unwrap(); + } + eprintln!(); + + Ok(()) +} diff --git a/examples/demo/src/thread_local.rs b/examples/demo/src/thread_local.rs deleted file mode 100644 index 43067acc1..000000000 --- a/examples/demo/src/thread_local.rs +++ /dev/null @@ -1,14 +0,0 @@ -pub fn test_thread_local() -> Result<(), ()> { - #[repr(align(0x10))] - struct Aligned(u8); - - thread_local! { - static THREAD_LOCAL: Aligned = const { Aligned(0x42) }; - } - - THREAD_LOCAL.with(|thread_local| { - assert_eq!(0x42, thread_local.0); - }); - - Ok(()) -}