Fake Rayon (#162)

Dummy implementation of Rayon's traits to avoid `#[cfg()]` and duplicated non-parallel code.
This commit is contained in:
Kornel 2019-01-28 18:35:42 +00:00 committed by Josh Holmer
parent 0d4a9e06d6
commit 33890cf0fd
4 changed files with 61 additions and 38 deletions

View file

@ -9,7 +9,6 @@ use png::STD_COMPRESSION;
use png::STD_FILTERS;
use png::STD_STRATEGY;
use png::STD_WINDOW;
#[cfg(feature = "parallel")]
use rayon::prelude::*;
use std::sync::mpsc::*;
use std::sync::Arc;
@ -63,10 +62,7 @@ impl Evaluator {
let best_result: Mutex<Option<(PngData, _, _)>> = Mutex::new(None);
// ends when sender is dropped
for (nth, (image, bias, is_reduction)) in from_channel.iter().enumerate() {
#[cfg(feature = "parallel")]
let filters_iter = STD_FILTERS.par_iter().with_max_len(1);
#[cfg(not(feature = "parallel"))]
let filters_iter = STD_FILTERS.iter();
filters_iter.for_each(|&f| {
if let Ok(idat_data) = deflate::deflate(

View file

@ -9,6 +9,8 @@ extern crate miniz_oxide;
extern crate num_cpus;
#[cfg(feature = "parallel")]
extern crate rayon;
#[cfg(not(feature = "parallel"))]
mod rayon;
extern crate rgb;
extern crate zopfli;
@ -21,7 +23,6 @@ use image::{DynamicImage, GenericImageView, ImageFormat, Pixel};
use png::PngImage;
use png::PngData;
use colors::BitDepth;
#[cfg(feature = "parallel")]
use rayon::prelude::*;
use std::collections::{HashMap, HashSet};
use std::fmt;
@ -569,10 +570,7 @@ fn optimize_png(png: &mut PngData, original_data: &[u8], opts: &Options) -> PngR
}
}
#[cfg(feature = "parallel")]
let filter_iter = filter.par_iter().with_max_len(1);
#[cfg(not(feature = "parallel"))]
let filter_iter = filter.iter();
let filters: HashMap<u8, Vec<u8>> = filter_iter
.map(|f| {
let png = png.clone();
@ -583,10 +581,7 @@ fn optimize_png(png: &mut PngData, original_data: &[u8], opts: &Options) -> PngR
let added_interlacing = opts.interlace == Some(1) && original_png.raw.ihdr.interlaced == 0;
let best_size = AtomicMin::new(if opts.force { None } else { Some(original_len) });
#[cfg(feature = "parallel")]
let results_iter = results.into_par_iter().with_max_len(1);
#[cfg(not(feature = "parallel"))]
let results_iter = results.into_iter();
let best = results_iter.filter_map(|trial| {
if deadline.passed() {
return None;
@ -635,20 +630,8 @@ fn optimize_png(png: &mut PngData, original_data: &[u8], opts: &Options) -> PngR
None
}
});
#[cfg(feature = "parallel")]
let best: Option<TrialWithData> =
best.reduce_with(|i, j| if i.1.len() <= j.1.len() { i } else { j });
#[cfg(not(feature = "parallel"))]
let best: Option<TrialWithData> = best.fold(None, |i, j| {
if let Some(i) = i {
if i.1.len() <= j.1.len() {
Some(i)
} else {
Some(j)
}
} else {
Some(j)
}
let best: Option<TrialWithData> = best.reduce_with(|i, j| {
if i.1.len() <= j.1.len() { i } else { j }
});
if let Some(better) = best {
@ -704,15 +687,10 @@ fn optimize_png(png: &mut PngData, original_data: &[u8], opts: &Options) -> PngR
}
}
let (old_png, new_png) = {
let old_png = || image::load_from_memory_with_format(original_data, ImageFormat::PNG);
let new_png = || image::load_from_memory_with_format(&output, ImageFormat::PNG);
#[cfg(feature = "parallel")]
let res = rayon::join(old_png, new_png);
#[cfg(not(feature = "parallel"))]
let res = (old_png(), new_png());
res
};
let (old_png, new_png) = rayon::join(
|| image::load_from_memory_with_format(original_data, ImageFormat::PNG),
|| image::load_from_memory_with_format(&output, ImageFormat::PNG)
);
if let Ok(new_png) = new_png {
if let Ok(old_png) = old_png {

53
src/rayon.rs Normal file
View file

@ -0,0 +1,53 @@
pub mod prelude {
pub use super::*;
}
pub trait ParallelIterator: Iterator + Sized {
fn with_max_len(self, _l: usize) -> Self {self}
fn reduce_with<OP>(mut self, op: OP) -> Option<Self::Item> where OP: Fn(Self::Item, Self::Item) -> Self::Item + Sync {
if let Some(a) = self.next() {
Some(self.fold(a, op))
} else {
None
}
}
}
pub trait IntoParallelIterator {
type Iter: Iterator<Item=Self::Item>;
type Item: Send;
fn into_par_iter(self) -> Self::Iter;
}
pub trait IntoParallelRefIterator<'data> {
type Iter: Iterator<Item=Self::Item>;
type Item: Send + 'data;
fn par_iter(&'data self) -> Self::Iter;
}
impl<I: IntoIterator> IntoParallelIterator for I where I::Item: Send {
type Iter = I::IntoIter;
type Item = I::Item;
fn into_par_iter(self) -> Self::Iter {
self.into_iter()
}
}
impl<'data, I: 'data + ?Sized> IntoParallelRefIterator<'data> for I
where &'data I: IntoParallelIterator
{
type Iter = <&'data I as IntoParallelIterator>::Iter;
type Item = <&'data I as IntoParallelIterator>::Item;
fn par_iter(&'data self) -> Self::Iter {
self.into_par_iter()
}
}
impl<I: Iterator> ParallelIterator for I {
}
pub fn join<A, B>(a: impl FnOnce() -> A, b: impl FnOnce() -> B) -> (A, B) {
(a(), b())
}

View file

@ -7,16 +7,12 @@ use colors::AlphaOptim;
use headers::IhdrData;
use png::PngImage;
use colors::ColorType;
#[cfg(feature = "parallel")]
use rayon::prelude::*;
pub fn try_alpha_reductions(png: Arc<PngImage>, alphas: &HashSet<AlphaOptim>, eval: &Evaluator) {
assert!(!alphas.is_empty());
let alphas = alphas.iter().collect::<Vec<_>>();
#[cfg(feature = "parallel")]
let alphas_iter = alphas.par_iter().with_max_len(1);
#[cfg(not(feature = "parallel"))]
let alphas_iter = alphas.iter();
alphas_iter
.filter_map(|&alpha| filtered_alpha_channel(&png, *alpha))
.for_each(|image| eval.try_image(Arc::new(image), 0.99));