Browse Source

Naive multi-threaded implementation

main v0.2.0
Unbewohnte 3 years ago
parent
commit
0d1569351e
  1. 2
      Cargo.lock
  2. 2
      Cargo.toml
  3. 12
      README.md
  4. 78
      src/main.rs

2
Cargo.lock generated

@ -223,7 +223,7 @@ checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c"
[[package]] [[package]]
name = "mandelplot" name = "mandelplot"
version = "0.1.1" version = "0.2.0"
dependencies = [ dependencies = [
"clap", "clap",
"image", "image",

2
Cargo.toml

@ -1,6 +1,6 @@
[package] [package]
name = "mandelplot" name = "mandelplot"
version = "0.1.1" version = "0.2.0"
license = "MIT" license = "MIT"
edition = "2021" edition = "2021"
authors = ["Unbewohnte <me@unbewohnte.xyz>"] authors = ["Unbewohnte <me@unbewohnte.xyz>"]

12
README.md

@ -19,5 +19,15 @@ OPTIONS:
- `-V, --version` => Print version information - `-V, --version` => Print version information
## Naive benchmarks
Singe-threaded (19200x10800) (pre v0.2.0)
- ./mandelplot -d 19200x10800 -p dark 70.06s user 0.14s system 99% cpu 1:10.24 total
Multi-threaded (19200x10800) (v0.2.0)
- ./target/release/mandelplot -d 19200x10800 -p dark 85.28s user 8.06s system 258% cpu 36.079 total
1:10 - 0:36 == 0:34 seconds saved
## TODO: ## TODO:
- generate image in parallel - ~~generate image in parallel~~
- zooming in

78
src/main.rs

@ -25,6 +25,7 @@ use image::ImageFormat;
use image::Rgb; use image::Rgb;
use image::RgbImage; use image::RgbImage;
use num::Complex; use num::Complex;
use std::sync::{Arc, Mutex};
/// z(n) = z(n-1)^2 + c /// z(n) = z(n-1)^2 + c
/// Returns amount of iterations to decide that z will escape to infinity /// Returns amount of iterations to decide that z will escape to infinity
@ -40,6 +41,33 @@ fn mandelbrot(c: num::Complex<f64>, iterations: u32) -> Option<u32> {
return None; return None;
} }
/// Converts a pixel in an image plane to the point in the imaginary plane
fn pixel_to_set_point(x: u32, y: u32, width: u32, height: u32, r_s: f64, r_e: f64, i_s: f64, i_e: f64) -> num::Complex<f64> {
return num::Complex::new(
r_s + (x as f64 / width as f64) * (r_e - r_s),
i_s + (y as f64 / height as f64) * (i_e - i_s),
)
}
/// Computes a gray color of the pixel that corresponds to the point in the imaginary plane based on
/// amount of iteraitons
fn compute_pixel_color(c: num::Complex<f64>, max_iter: u32, palette: u8) -> u8 {
let pixel_color: u8;
if let Some(iterations) = mandelbrot(c, max_iter) {
pixel_color = ((iterations as f64 / 255.0).sin() * 255.0) as u8;
} else {
if palette == 0 {
// light
pixel_color = 255;
} else {
// dark
pixel_color = 0;
}
}
return pixel_color;
}
fn main() { fn main() {
const RE_START: f64 = -2.5; const RE_START: f64 = -2.5;
const RE_END: f64 = 1.5; const RE_END: f64 = 1.5;
@ -53,7 +81,7 @@ fn main() {
let mut palette: u8 = 0; let mut palette: u8 = 0;
let matches = clap::App::new("mandelplot") let matches = clap::App::new("mandelplot")
.version("0.1.1") .version("0.2.0")
.author("Kasyanov Nikolay Alexeyevich (Unbewohnte)") .author("Kasyanov Nikolay Alexeyevich (Unbewohnte)")
.arg( .arg(
Arg::new("max_iter") Arg::new("max_iter")
@ -93,6 +121,7 @@ fn main() {
) )
.get_matches(); .get_matches();
// process given options
if let Some(arg_max_iter) = matches.value_of("max_iter") { if let Some(arg_max_iter) = matches.value_of("max_iter") {
max_iter = arg_max_iter.parse::<u32>().unwrap(); max_iter = arg_max_iter.parse::<u32>().unwrap();
} }
@ -123,38 +152,47 @@ fn main() {
} }
} }
let mut img = RgbImage::new(width, height);
for y in 0..height { let img = Arc::new(Mutex::new(RgbImage::new(width, height)));
// run the algorithm in a naive multi-threaded way
const AMOUNT_OF_THREADS: usize = 8;
let thread_work = (height as f32 / AMOUNT_OF_THREADS as f32) as u32;
let mut threads = Vec::with_capacity(AMOUNT_OF_THREADS);
let mut from: u32 = 0;
for _ in 0..AMOUNT_OF_THREADS {
let img_copy = img.clone();
threads.push(std::thread::spawn(move || {
for y in from..from+thread_work {
for x in 0..width { for x in 0..width {
let c = num::Complex::new( let c = pixel_to_set_point(x, y, width, height, RE_START, RE_END, IM_START, IM_END);
RE_START + (x as f64 / width as f64) * (RE_END - RE_START), let pixel_color = compute_pixel_color(c, max_iter, palette);
IM_START + (y as f64 / height as f64) * (IM_END - IM_START),
);
let pixel_color: u8; img_copy.lock().unwrap().put_pixel(x, y, Rgb([pixel_color, pixel_color, pixel_color]));
if let Some(iterations) = mandelbrot(c, max_iter) { }
pixel_color = ((iterations as f64 / 255.0).sin() * 255.0) as u8;
} else {
if palette == 0 {
// light
pixel_color = 255;
} else {
// dark
pixel_color = 0;
} }
}));
from += thread_work;
} }
img.put_pixel(x, y, Rgb([pixel_color, pixel_color, pixel_color])); // wait for everyone to finish
for thread in threads {
match thread.join() {
Ok(_) => {}
Err(e) => { eprintln!("Error waiting for thread to finish: {:?}", e); }
} }
} }
match img.save_with_format(image_name + ".png", ImageFormat::Png) { // save image
match img.lock().unwrap().save_with_format(image_name + ".png", ImageFormat::Png) {
Ok(_) => { Ok(_) => {
println!("Saved") println!("Saved")
} }
Err(e) => { Err(e) => {
eprintln!("Could not save image: {}", e) eprintln!("Could not save image: {}", e)
} }
} };
} }

Loading…
Cancel
Save