- Benchmark framework glassbench replaced by criterion.

- Added report with results of benchmarks for `wasm32-wasi` target.
This commit is contained in:
Kirill Kuzminykh
2023-01-21 22:06:51 +04:00
parent 60367f1b44
commit 661777b0b4
23 changed files with 1323 additions and 1825 deletions
+7
View File
@@ -1,3 +1,10 @@
## [Unreleased] - ReleaseDate
## Benchmarks
- Benchmark framework `glassbench` replaced by `criterion`.
- Added report with results of benchmarks for `wasm32-wasi` target.
## [2.4.0] - 2022-12-11
### Crate
Generated
+215 -793
View File
File diff suppressed because it is too large Load Diff
+11 -2
View File
@@ -28,13 +28,22 @@ for_test = []
[dev-dependencies]
fast_image_resize = { path = ".", features = ["for_test"] }
glassbench = "0.3.3"
image = "0.24.5"
resize = "0.7.4"
rgb = "0.8.34"
png = "0.17.7"
nix = { version = "0.26.1", default-features = false, features = ["sched"] }
testing = { path = "testing" }
serde = { version = "1.0", features = ["serde_derive"] }
serde_json = "1.0"
walkdir = "2"
itertools = "0.10.5"
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
criterion = { version = "0.4.0", default-features = false, features = ["cargo_bench_support", "rayon"] }
nix = { version = "0.26.1", default-features = false, features = ["sched"] }
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
criterion = { version = "0.4.0", default-features = false, features = ["cargo_bench_support"] }
[[bench]]
+9 -3
View File
@@ -41,10 +41,10 @@ In addition, the crate contains functions `create_gamma_22_mapper()`
and `create_srgb_mapper()` to create instance of `PixelComponentMapper`
that converts images from sRGB or gamma 2.2 into linear colorspace and back.
## Some benchmarks
## Some benchmarks for x86_64
- [All x86-64 benchmarks.](https://github.com/Cykooz/fast_image_resize/blob/main/benchmarks_x86-64.md)
- [All arm64 benchmarks.](https://github.com/Cykooz/fast_image_resize/blob/main/benchmarks_arm64.md)
- [All x86_64 benchmarks.](https://github.com/Cykooz/fast_image_resize/blob/main/benchmarks-x86_64.md)
- [All arm64 benchmarks.](https://github.com/Cykooz/fast_image_resize/blob/main/benchmarks-arm64.md)
Rust libraries used to compare of resizing speed:
@@ -60,6 +60,7 @@ Pipeline:
- Source image [nasa-4928x3279.png](https://github.com/Cykooz/fast_image_resize/blob/main/data/nasa-4928x3279.png)
- Numbers in table is mean duration of image resizing in milliseconds.
[comment]: <> (bench_compare_rgb start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|------------|:-------:|:--------:|:----------:|:--------:|
| image | 20.66 | 82.74 | 141.56 | 199.96 |
@@ -67,6 +68,7 @@ Pipeline:
| fir rust | 0.28 | 39.57 | 67.00 | 98.50 |
| fir sse4.1 | 0.28 | 9.63 | 14.13 | 19.84 |
| fir avx2 | 0.28 | 7.73 | 9.67 | 14.66 |
[comment]: <> (bench_compare_rgb end)
### Resize RGBA8 image (U8x4) 4928x3279 => 852x567
@@ -79,12 +81,14 @@ Pipeline:
- Numbers in table is mean duration of image resizing in milliseconds.
- The `image` crate does not support multiplying and dividing by alpha channel.
[comment]: <> (bench_compare_rgba start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|------------|:-------:|:--------:|:----------:|:--------:|
| resize | - | 65.74 | 130.18 | 194.13 |
| fir rust | 0.19 | 35.61 | 52.50 | 75.76 |
| fir sse4.1 | 0.19 | 13.19 | 17.25 | 22.62 |
| fir avx2 | 0.19 | 9.57 | 11.97 | 16.37 |
[comment]: <> (bench_compare_rgba end)
### Resize L8 image (U8) 4928x3279 => 852x567
@@ -96,6 +100,7 @@ Pipeline:
has converted into grayscale image with one byte per pixel.
- Numbers in table is mean duration of image resizing in milliseconds.
[comment]: <> (bench_compare_l start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|------------|:-------:|:--------:|:----------:|:--------:|
| image | 16.97 | 48.47 | 77.55 | 107.48 |
@@ -103,6 +108,7 @@ Pipeline:
| fir rust | 0.15 | 13.44 | 14.73 | 22.58 |
| fir sse4.1 | 0.15 | 4.99 | 5.28 | 8.05 |
| fir avx2 | 0.15 | 7.09 | 5.18 | 8.60 |
[comment]: <> (bench_compare_l end)
## Examples
+61 -42
View File
@@ -1,7 +1,5 @@
use std::num::NonZeroU32;
use glassbench::*;
use fast_image_resize::MulDiv;
use fast_image_resize::PixelType;
use fast_image_resize::{CpuExtensions, Image};
@@ -23,7 +21,13 @@ fn get_src_image(
Image::from_vec_u8(width, height, buffer, pixel_type).unwrap()
}
fn multiplies_alpha(bench: &mut Bench, pixel_type: PixelType, cpu_extensions: CpuExtensions) {
fn multiplies_alpha(
bench_group: &mut utils::BenchGroup,
pixel_type: PixelType,
cpu_extensions: CpuExtensions,
ext_name: &str,
) {
let sample_size = 100;
let width = NonZeroU32::new(4096).unwrap();
let height = NonZeroU32::new(2048).unwrap();
let pixel: &[u8] = match pixel_type {
@@ -42,10 +46,13 @@ fn multiplies_alpha(bench: &mut Bench, pixel_type: PixelType, cpu_extensions: Cp
alpha_mul_div.set_cpu_extensions(cpu_extensions);
}
bench.task(
format!("Multiplies alpha {:?} {:?}", pixel_type, cpu_extensions),
|task| {
task.iter(|| {
utils::bench(
bench_group,
sample_size,
format!("Multiplies alpha {:?}", pixel_type),
ext_name,
|bencher| {
bencher.iter(|| {
alpha_mul_div
.multiply_alpha(&src_view, &mut dst_view)
.unwrap();
@@ -53,22 +60,29 @@ fn multiplies_alpha(bench: &mut Bench, pixel_type: PixelType, cpu_extensions: Cp
},
);
bench.task(
format!(
"Multiplies alpha inplace {:?} {:?}",
pixel_type, cpu_extensions
),
|task| {
let mut data = get_src_image(width, height, pixel_type, pixel);
let mut view = data.view_mut();
task.iter(|| {
let src_image = get_src_image(width, height, pixel_type, pixel);
utils::bench(
bench_group,
sample_size,
format!("Multiplies alpha inplace {:?}", pixel_type),
ext_name,
|bencher| {
let mut image = src_image.copy();
let mut view = image.view_mut();
bencher.iter(|| {
alpha_mul_div.multiply_alpha_inplace(&mut view).unwrap();
})
},
);
}
fn divides_alpha(bench: &mut Bench, pixel_type: PixelType, cpu_extensions: CpuExtensions) {
fn divides_alpha(
bench_group: &mut utils::BenchGroup,
pixel_type: PixelType,
cpu_extensions: CpuExtensions,
ext_name: &str,
) {
let sample_size = 100;
let width = NonZeroU32::new(4095).unwrap();
let height = NonZeroU32::new(2048).unwrap();
let pixel: &[u8] = match pixel_type {
@@ -87,10 +101,13 @@ fn divides_alpha(bench: &mut Bench, pixel_type: PixelType, cpu_extensions: CpuEx
alpha_mul_div.set_cpu_extensions(cpu_extensions);
}
bench.task(
format!("Divides alpha {:?} {:?}", pixel_type, cpu_extensions),
|task| {
task.iter(|| {
utils::bench(
bench_group,
sample_size,
format!("Divides alpha {:?}", pixel_type),
ext_name,
|bencher| {
bencher.iter(|| {
alpha_mul_div
.divide_alpha(&src_view, &mut dst_view)
.unwrap();
@@ -98,50 +115,52 @@ fn divides_alpha(bench: &mut Bench, pixel_type: PixelType, cpu_extensions: CpuEx
},
);
bench.task(
format!(
"Divides alpha inplace {:?} {:?}",
pixel_type, cpu_extensions
),
|task| {
let mut data = get_src_image(width, height, pixel_type, pixel);
let mut view = data.view_mut();
task.iter(|| {
let src_image = get_src_image(width, height, pixel_type, pixel);
utils::bench(
bench_group,
sample_size,
format!("Divides alpha inplace {:?}", pixel_type),
ext_name,
|bencher| {
let mut image = src_image.copy();
let mut view = image.view_mut();
bencher.iter(|| {
alpha_mul_div.divide_alpha_inplace(&mut view).unwrap();
})
},
);
}
fn bench_alpha(bench: &mut Bench) {
fn bench_alpha(bench_group: &mut utils::BenchGroup) {
let pixel_types = [
PixelType::U8x2,
PixelType::U8x4,
PixelType::U16x2,
PixelType::U16x4,
];
let mut cpu_extensions = vec![CpuExtensions::None];
let mut cpu_ext_and_name = vec![(CpuExtensions::None, "rust")];
#[cfg(target_arch = "x86_64")]
{
cpu_extensions.push(CpuExtensions::Sse4_1);
cpu_extensions.push(CpuExtensions::Avx2);
cpu_ext_and_name.push((CpuExtensions::Sse4_1, "sse4.1"));
cpu_ext_and_name.push((CpuExtensions::Avx2, "avx2"));
}
#[cfg(target_arch = "aarch64")]
{
cpu_extensions.push(CpuExtensions::Neon);
cpu_ext_and_name.push((CpuExtensions::Neon, "neon"));
}
for pixel_type in pixel_types {
for &extensions in cpu_extensions.iter() {
println!("Mul {:?} {:?}", pixel_type, extensions);
multiplies_alpha(bench, pixel_type, extensions);
for &(cpu_ext, ext_name) in cpu_ext_and_name.iter() {
multiplies_alpha(bench_group, pixel_type, cpu_ext, ext_name);
}
}
for pixel_type in pixel_types {
for &extensions in cpu_extensions.iter() {
println!("Div {:?} {:?}", pixel_type, extensions);
divides_alpha(bench, pixel_type, extensions);
for &(cpu_ext, ext_name) in cpu_ext_and_name.iter() {
divides_alpha(bench_group, pixel_type, cpu_ext, ext_name);
}
}
}
bench_main!("Bench Alpha", bench_alpha,);
fn main() {
let res = utils::run_bench(bench_alpha, "Bench Alpha");
println!("{}", utils::build_md_table(&res));
}
+6 -6
View File
@@ -1,12 +1,10 @@
use glassbench::*;
use fast_image_resize::pixels::U8x3;
use fast_image_resize::{create_srgb_mapper, Image};
use testing::PixelTestingExt;
mod utils;
pub fn bench_color_mapper(bench: &mut Bench) {
pub fn bench_color_mapper(bench_group: &mut utils::BenchGroup) {
let src_image = U8x3::load_big_src_image();
let mut dst_image = Image::new(
src_image.width(),
@@ -16,11 +14,13 @@ pub fn bench_color_mapper(bench: &mut Bench) {
let src_view = src_image.view();
let mut dst_view = dst_image.view_mut();
let mapper = create_srgb_mapper();
bench.task("SRGB U8x3 => RGB U8x3", |task| {
task.iter(|| {
bench_group.bench_function("SRGB U8x3 => RGB U8x3", |bencher| {
bencher.iter(|| {
mapper.forward_map(&src_view, &mut dst_view).unwrap();
})
});
}
bench_main!("Bench color mappers", bench_color_mapper,);
fn main() {
utils::run_bench(bench_color_mapper, "Color mapper");
}
+16 -107
View File
@@ -1,117 +1,26 @@
use std::num::NonZeroU32;
use std::thread::sleep;
use std::time::Duration;
use glassbench::*;
use image::imageops;
use resize::Pixel::Gray8;
use rgb::alt::Gray;
use rgb::FromSlice;
use fast_image_resize::pixels::U8;
use fast_image_resize::Image;
use fast_image_resize::{CpuExtensions, FilterType, ResizeAlg, Resizer};
use testing::PixelTestingExt;
mod utils;
pub fn bench_downscale_l(bench: &mut Bench) {
let src_image = U8::load_big_image().to_luma8();
let new_width = NonZeroU32::new(852).unwrap();
let new_height = NonZeroU32::new(567).unwrap();
let alg_names = ["Nearest", "Bilinear", "CatmullRom", "Lanczos3"];
// image crate
// https://crates.io/crates/image
for alg_name in alg_names {
let filter = match alg_name {
"Nearest" => imageops::Nearest,
"Bilinear" => imageops::Triangle,
"CatmullRom" => imageops::CatmullRom,
"Lanczos3" => imageops::Lanczos3,
_ => continue,
};
bench.task(format!("image - {}", alg_name), |task| {
task.iter(|| {
imageops::resize(&src_image, new_width.get(), new_height.get(), filter);
})
});
}
// resize crate
// https://crates.io/crates/resize
for alg_name in alg_names {
let resize_src_image = src_image.as_raw().as_gray();
let mut dst = vec![Gray(0u8); (new_width.get() * new_height.get()) as usize];
bench.task(format!("resize - {}", alg_name), |task| {
let filter = match alg_name {
"Nearest" => {
// resizer doesn't support "nearest" algorithm
task.iter(|| sleep(Duration::new(0, 1)));
return;
}
"Bilinear" => resize::Type::Triangle,
"CatmullRom" => resize::Type::Catrom,
"Lanczos3" => resize::Type::Lanczos3,
_ => return,
};
let mut resize = resize::new(
src_image.width() as usize,
src_image.height() as usize,
new_width.get() as usize,
new_height.get() as usize,
Gray8,
filter,
)
.unwrap();
task.iter(|| {
resize.resize(resize_src_image, &mut dst).unwrap();
})
});
}
// fast_image_resize crate;
let mut cpu_ext_and_name = vec![(CpuExtensions::None, "rust")];
#[cfg(target_arch = "x86_64")]
{
cpu_ext_and_name.push((CpuExtensions::Sse4_1, "sse4.1"));
cpu_ext_and_name.push((CpuExtensions::Avx2, "avx2"));
}
#[cfg(target_arch = "aarch64")]
{
cpu_ext_and_name.push((CpuExtensions::Neon, "neon"));
}
for (cpu_ext, ext_name) in cpu_ext_and_name {
for alg_name in alg_names {
let src_image_data = U8::load_big_src_image();
let src_view = src_image_data.view();
let mut dst_image = Image::new(new_width, new_height, src_view.pixel_type());
let mut dst_view = dst_image.view_mut();
let resize_alg = match alg_name {
"Nearest" => ResizeAlg::Nearest,
"Bilinear" => ResizeAlg::Convolution(FilterType::Bilinear),
"CatmullRom" => ResizeAlg::Convolution(FilterType::CatmullRom),
"Lanczos3" => ResizeAlg::Convolution(FilterType::Lanczos3),
_ => return,
};
let mut fast_resizer = Resizer::new(resize_alg);
unsafe {
fast_resizer.reset_internal_buffers();
fast_resizer.set_cpu_extensions(cpu_ext);
}
bench.task(format!("fir {} - {}", ext_name, alg_name), |task| {
task.iter(|| {
fast_resizer.resize(&src_view, &mut dst_view).unwrap();
})
});
}
}
utils::print_md_table(bench);
pub fn bench_compare_l(bench_group: &mut utils::BenchGroup) {
type P = U8;
let src_image = P::load_big_image().to_luma8();
utils::image_resize(bench_group, &src_image);
utils::resize_resize(
bench_group,
Gray8,
src_image.as_raw().as_gray(),
src_image.width(),
src_image.height(),
);
utils::fir_resize::<P>(bench_group);
}
bench_main!("Compare resize of U8 image", bench_downscale_l,);
fn main() {
let res = utils::run_bench(bench_compare_l, "Compare resize of U8 image");
utils::print_and_write_compare_result(&res);
}
+16 -107
View File
@@ -1,117 +1,26 @@
use std::num::NonZeroU32;
use std::thread::sleep;
use std::time::Duration;
use glassbench::*;
use image::imageops;
use resize::Pixel::Gray16;
use rgb::alt::Gray;
use rgb::FromSlice;
use fast_image_resize::pixels::U16;
use fast_image_resize::Image;
use fast_image_resize::{CpuExtensions, FilterType, ResizeAlg, Resizer};
use testing::PixelTestingExt;
mod utils;
pub fn bench_downscale_l16(bench: &mut Bench) {
let src_image = U16::load_big_image().to_luma16();
let new_width = NonZeroU32::new(852).unwrap();
let new_height = NonZeroU32::new(567).unwrap();
let alg_names = ["Nearest", "Bilinear", "CatmullRom", "Lanczos3"];
// image crate
// https://crates.io/crates/image
for alg_name in alg_names {
let filter = match alg_name {
"Nearest" => imageops::Nearest,
"Bilinear" => imageops::Triangle,
"CatmullRom" => imageops::CatmullRom,
"Lanczos3" => imageops::Lanczos3,
_ => continue,
};
bench.task(format!("image - {}", alg_name), |task| {
task.iter(|| {
imageops::resize(&src_image, new_width.get(), new_height.get(), filter);
})
});
}
// resize crate
// https://crates.io/crates/resize
for alg_name in alg_names {
let resize_src_image = src_image.as_raw().as_gray();
let mut dst = vec![Gray(0u16); (new_width.get() * new_height.get()) as usize];
bench.task(format!("resize - {}", alg_name), |task| {
let filter = match alg_name {
"Nearest" => {
// resizer doesn't support "nearest" algorithm
task.iter(|| sleep(Duration::new(0, 1)));
return;
}
"Bilinear" => resize::Type::Triangle,
"CatmullRom" => resize::Type::Catrom,
"Lanczos3" => resize::Type::Lanczos3,
_ => return,
};
let mut resize = resize::new(
src_image.width() as usize,
src_image.height() as usize,
new_width.get() as usize,
new_height.get() as usize,
Gray16,
filter,
)
.unwrap();
task.iter(|| {
resize.resize(resize_src_image, &mut dst).unwrap();
})
});
}
// fast_image_resize crate;
let mut cpu_ext_and_name = vec![(CpuExtensions::None, "rust")];
#[cfg(target_arch = "x86_64")]
{
cpu_ext_and_name.push((CpuExtensions::Sse4_1, "sse4.1"));
cpu_ext_and_name.push((CpuExtensions::Avx2, "avx2"));
}
#[cfg(target_arch = "aarch64")]
{
cpu_ext_and_name.push((CpuExtensions::Neon, "neon"));
}
for (cpu_ext, ext_name) in cpu_ext_and_name {
for alg_name in alg_names {
let src_image_data = U16::load_big_src_image();
let src_view = src_image_data.view();
let mut dst_image = Image::new(new_width, new_height, src_view.pixel_type());
let mut dst_view = dst_image.view_mut();
let resize_alg = match alg_name {
"Nearest" => ResizeAlg::Nearest,
"Bilinear" => ResizeAlg::Convolution(FilterType::Bilinear),
"CatmullRom" => ResizeAlg::Convolution(FilterType::CatmullRom),
"Lanczos3" => ResizeAlg::Convolution(FilterType::Lanczos3),
_ => return,
};
let mut fast_resizer = Resizer::new(resize_alg);
unsafe {
fast_resizer.reset_internal_buffers();
fast_resizer.set_cpu_extensions(cpu_ext);
}
bench.task(format!("fir {} - {}", ext_name, alg_name), |task| {
task.iter(|| {
fast_resizer.resize(&src_view, &mut dst_view).unwrap();
})
});
}
}
utils::print_md_table(bench);
pub fn bench_downscale_l16(bench_group: &mut utils::BenchGroup) {
type P = U16;
let src_image = P::load_big_image().to_luma16();
utils::image_resize(bench_group, &src_image);
utils::resize_resize(
bench_group,
Gray16,
src_image.as_raw().as_gray(),
src_image.width(),
src_image.height(),
);
utils::fir_resize::<P>(bench_group);
}
bench_main!("Compare resize of U16 image", bench_downscale_l16,);
fn main() {
let res = utils::run_bench(bench_downscale_l16, "Compare resize of U16 image");
utils::print_and_write_compare_result(&res);
}
+6 -75
View File
@@ -1,81 +1,12 @@
use std::num::NonZeroU32;
use glassbench::*;
use fast_image_resize::pixels::U8x2;
use fast_image_resize::{CpuExtensions, FilterType, Image, MulDiv, PixelType, ResizeAlg, Resizer};
use testing::PixelTestingExt;
mod utils;
pub fn bench_downscale_la(bench: &mut Bench) {
let new_width = NonZeroU32::new(852).unwrap();
let new_height = NonZeroU32::new(567).unwrap();
let alg_names = ["Nearest", "Bilinear", "CatmullRom", "Lanczos3"];
// fast_image_resize crate;
let mut cpu_ext_and_name = vec![(CpuExtensions::None, "rust")];
#[cfg(target_arch = "x86_64")]
{
cpu_ext_and_name.push((CpuExtensions::Sse4_1, "sse4.1"));
cpu_ext_and_name.push((CpuExtensions::Avx2, "avx2"));
}
#[cfg(target_arch = "aarch64")]
{
cpu_ext_and_name.push((CpuExtensions::Neon, "neon"));
}
for (cpu_ext, ext_name) in cpu_ext_and_name {
for alg_name in alg_names {
let resize_alg = match alg_name {
"Nearest" => ResizeAlg::Nearest,
"Bilinear" => ResizeAlg::Convolution(FilterType::Bilinear),
"CatmullRom" => ResizeAlg::Convolution(FilterType::CatmullRom),
"Lanczos3" => ResizeAlg::Convolution(FilterType::Lanczos3),
_ => return,
};
let src_image = U8x2::load_big_src_image();
let src_view = src_image.view();
let mut premultiplied_src_image =
Image::new(src_image.width(), src_image.height(), src_view.pixel_type());
let mut dst_image = Image::new(new_width, new_height, PixelType::U8x2);
let mut dst_view = dst_image.view_mut();
let mut mul_div = MulDiv::default();
let mut fast_resizer = Resizer::new(resize_alg);
unsafe {
fast_resizer.reset_internal_buffers();
fast_resizer.set_cpu_extensions(cpu_ext);
mul_div.set_cpu_extensions(cpu_ext);
}
bench.task(
format!("fir {} - {}", ext_name, alg_name),
|task| match resize_alg {
ResizeAlg::Nearest => {
task.iter(|| {
fast_resizer.resize(&src_view, &mut dst_view).unwrap();
});
}
_ => {
task.iter(|| {
let mut premultiplied_view = premultiplied_src_image.view_mut();
mul_div
.multiply_alpha(&src_view, &mut premultiplied_view)
.unwrap();
fast_resizer
.resize(&premultiplied_view.into(), &mut dst_view)
.unwrap();
mul_div.divide_alpha_inplace(&mut dst_view).unwrap();
});
}
},
);
}
}
utils::print_md_table(bench);
pub fn bench_downscale_la(bench_group: &mut utils::BenchGroup) {
utils::fir_resize_with_alpha::<U8x2>(bench_group);
}
bench_main!("Compare resize of LA image", bench_downscale_la,);
fn main() {
let res = utils::run_bench(bench_downscale_la, "Compare resize of LA image");
utils::print_and_write_compare_result(&res);
}
+6 -73
View File
@@ -1,79 +1,12 @@
use std::num::NonZeroU32;
use glassbench::*;
use fast_image_resize::pixels::U16x2;
use fast_image_resize::{CpuExtensions, FilterType, Image, MulDiv, PixelType, ResizeAlg, Resizer};
use testing::PixelTestingExt;
mod utils;
pub fn bench_downscale_la16(bench: &mut Bench) {
let new_width = NonZeroU32::new(852).unwrap();
let new_height = NonZeroU32::new(567).unwrap();
let alg_names = ["Nearest", "Bilinear", "CatmullRom", "Lanczos3"];
// fast_image_resize crate;
let mut cpu_ext_and_name = vec![(CpuExtensions::None, "rust")];
#[cfg(target_arch = "x86_64")]
{
cpu_ext_and_name.push((CpuExtensions::Sse4_1, "sse4.1"));
cpu_ext_and_name.push((CpuExtensions::Avx2, "avx2"));
}
#[cfg(target_arch = "aarch64")]
{
cpu_ext_and_name.push((CpuExtensions::Neon, "neon"));
}
for (cpu_ext, ext_name) in cpu_ext_and_name {
for alg_name in alg_names {
let resize_alg = match alg_name {
"Nearest" => ResizeAlg::Nearest,
"Bilinear" => ResizeAlg::Convolution(FilterType::Bilinear),
"CatmullRom" => ResizeAlg::Convolution(FilterType::CatmullRom),
"Lanczos3" => ResizeAlg::Convolution(FilterType::Lanczos3),
_ => return,
};
let src_image = U16x2::load_big_src_image();
let src_view = src_image.view();
let mut premultiplied_src_image = Image::new(
src_image.width(),
src_image.height(),
src_image.pixel_type(),
);
let mut dst_image = Image::new(new_width, new_height, PixelType::U16x2);
let mut dst_view = dst_image.view_mut();
let mut mul_div = MulDiv::default();
let mut fast_resizer = Resizer::new(resize_alg);
unsafe {
fast_resizer.reset_internal_buffers();
fast_resizer.set_cpu_extensions(cpu_ext);
mul_div.set_cpu_extensions(cpu_ext);
}
bench.task(format!("fir {} - {}", ext_name, alg_name), |task| {
task.iter(|| match resize_alg {
ResizeAlg::Nearest => {
fast_resizer.resize(&src_view, &mut dst_view).unwrap();
}
_ => {
let mut premultiplied_view = premultiplied_src_image.view_mut();
mul_div
.multiply_alpha(&src_view, &mut premultiplied_view)
.unwrap();
fast_resizer
.resize(&premultiplied_view.into(), &mut dst_view)
.unwrap();
mul_div.divide_alpha_inplace(&mut dst_view).unwrap();
}
})
});
}
}
utils::print_md_table(bench);
pub fn bench_downscale_la16(bench_group: &mut utils::BenchGroup) {
utils::fir_resize_with_alpha::<U16x2>(bench_group);
}
bench_main!("Compare resize of LA16 image", bench_downscale_la16,);
fn main() {
let res = utils::run_bench(bench_downscale_la16, "Compare resize of LA16 image");
utils::print_and_write_compare_result(&res);
}
+17 -107
View File
@@ -1,116 +1,26 @@
use std::num::NonZeroU32;
use std::thread::sleep;
use std::time::Duration;
use glassbench::*;
use image::imageops;
use resize::Pixel::RGB8;
use rgb::{FromSlice, RGB};
use rgb::FromSlice;
use fast_image_resize::pixels::U8x3;
use fast_image_resize::Image;
use fast_image_resize::{CpuExtensions, FilterType, ResizeAlg, Resizer};
use testing::PixelTestingExt;
mod utils;
pub fn bench_downscale_rgb(bench: &mut Bench) {
let src_image = U8x3::load_big_image().to_rgb8();
let new_width = NonZeroU32::new(852).unwrap();
let new_height = NonZeroU32::new(567).unwrap();
let alg_names = ["Nearest", "Bilinear", "CatmullRom", "Lanczos3"];
// image crate
// https://crates.io/crates/image
for alg_name in alg_names {
let filter = match alg_name {
"Nearest" => imageops::Nearest,
"Bilinear" => imageops::Triangle,
"CatmullRom" => imageops::CatmullRom,
"Lanczos3" => imageops::Lanczos3,
_ => continue,
};
bench.task(format!("image - {}", alg_name), |task| {
task.iter(|| {
imageops::resize(&src_image, new_width.get(), new_height.get(), filter);
})
});
}
// resize crate
// https://crates.io/crates/resize
for alg_name in alg_names {
let resize_src_image = src_image.as_raw().as_rgb();
let mut dst = vec![RGB::new(0, 0, 0); (new_width.get() * new_height.get()) as usize];
bench.task(format!("resize - {}", alg_name), |task| {
let filter = match alg_name {
"Nearest" => {
// resizer doesn't support "nearest" algorithm
task.iter(|| sleep(Duration::new(0, 1)));
return;
}
"Bilinear" => resize::Type::Triangle,
"CatmullRom" => resize::Type::Catrom,
"Lanczos3" => resize::Type::Lanczos3,
_ => return,
};
let mut resize = resize::new(
src_image.width() as usize,
src_image.height() as usize,
new_width.get() as usize,
new_height.get() as usize,
RGB8,
filter,
)
.unwrap();
task.iter(|| {
resize.resize(resize_src_image, &mut dst).unwrap();
})
});
}
// fast_image_resize crate;
let mut cpu_ext_and_name = vec![(CpuExtensions::None, "rust")];
#[cfg(target_arch = "x86_64")]
{
cpu_ext_and_name.push((CpuExtensions::Sse4_1, "sse4.1"));
cpu_ext_and_name.push((CpuExtensions::Avx2, "avx2"));
}
#[cfg(target_arch = "aarch64")]
{
cpu_ext_and_name.push((CpuExtensions::Neon, "neon"));
}
for (cpu_ext, ext_name) in cpu_ext_and_name {
for alg_name in alg_names {
let src_image_data = U8x3::load_big_src_image();
let src_view = src_image_data.view();
let mut dst_image = Image::new(new_width, new_height, src_view.pixel_type());
let mut dst_view = dst_image.view_mut();
let resize_alg = match alg_name {
"Nearest" => ResizeAlg::Nearest,
"Bilinear" => ResizeAlg::Convolution(FilterType::Bilinear),
"CatmullRom" => ResizeAlg::Convolution(FilterType::CatmullRom),
"Lanczos3" => ResizeAlg::Convolution(FilterType::Lanczos3),
_ => return,
};
let mut fast_resizer = Resizer::new(resize_alg);
unsafe {
fast_resizer.reset_internal_buffers();
fast_resizer.set_cpu_extensions(cpu_ext);
}
bench.task(format!("fir {} - {}", ext_name, alg_name), |task| {
task.iter(|| {
fast_resizer.resize(&src_view, &mut dst_view).unwrap();
})
});
}
}
utils::print_md_table(bench);
pub fn bench_downscale_rgb(bench_group: &mut utils::BenchGroup) {
type P = U8x3;
let src_image = P::load_big_image().to_rgb8();
utils::image_resize(bench_group, &src_image);
utils::resize_resize(
bench_group,
RGB8,
src_image.as_raw().as_rgb(),
src_image.width(),
src_image.height(),
);
utils::fir_resize::<P>(bench_group);
}
bench_main!("Compare resize of RGB image", bench_downscale_rgb,);
fn main() {
let res = utils::run_bench(bench_downscale_rgb, "Compare resize of RGB image");
utils::print_and_write_compare_result(&res);
}
+17 -108
View File
@@ -1,117 +1,26 @@
use std::num::NonZeroU32;
use std::thread::sleep;
use std::time::Duration;
use glassbench::*;
use image::imageops;
use resize::Pixel::RGB16;
use rgb::{FromSlice, RGB};
use rgb::FromSlice;
use fast_image_resize::pixels::U16x3;
use fast_image_resize::Image;
use fast_image_resize::{CpuExtensions, FilterType, ResizeAlg, Resizer};
use testing::PixelTestingExt;
mod utils;
pub fn bench_downscale_rgb16(bench: &mut Bench) {
let src_image = U16x3::load_big_image().to_rgb16();
let new_width = NonZeroU32::new(852).unwrap();
let new_height = NonZeroU32::new(567).unwrap();
let alg_names = ["Nearest", "Bilinear", "CatmullRom", "Lanczos3"];
// image crate
// https://crates.io/crates/image
for alg_name in alg_names {
let filter = match alg_name {
"Nearest" => imageops::Nearest,
"Bilinear" => imageops::Triangle,
"CatmullRom" => imageops::CatmullRom,
"Lanczos3" => imageops::Lanczos3,
_ => continue,
};
bench.task(format!("image - {}", alg_name), |task| {
task.iter(|| {
imageops::resize(&src_image, new_width.get(), new_height.get(), filter);
})
});
}
// resize crate
// https://crates.io/crates/resize
for alg_name in alg_names {
let resize_src_image = src_image.as_raw().as_rgb();
let mut dst =
vec![RGB::new(0u16, 0u16, 0u16); (new_width.get() * new_height.get()) as usize];
bench.task(format!("resize - {}", alg_name), |task| {
let filter = match alg_name {
"Nearest" => {
// resizer doesn't support "nearest" algorithm
task.iter(|| sleep(Duration::new(0, 1)));
return;
}
"Bilinear" => resize::Type::Triangle,
"CatmullRom" => resize::Type::Catrom,
"Lanczos3" => resize::Type::Lanczos3,
_ => return,
};
let mut resize = resize::new(
src_image.width() as usize,
src_image.height() as usize,
new_width.get() as usize,
new_height.get() as usize,
RGB16,
filter,
)
.unwrap();
task.iter(|| {
resize.resize(resize_src_image, &mut dst).unwrap();
})
});
}
// fast_image_resize crate;
let src_image_data = U16x3::load_big_src_image();
let mut cpu_ext_and_name = vec![(CpuExtensions::None, "rust")];
#[cfg(target_arch = "x86_64")]
{
cpu_ext_and_name.push((CpuExtensions::Sse4_1, "sse4.1"));
cpu_ext_and_name.push((CpuExtensions::Avx2, "avx2"));
}
#[cfg(target_arch = "aarch64")]
{
cpu_ext_and_name.push((CpuExtensions::Neon, "neon"));
}
for (cpu_ext, ext_name) in cpu_ext_and_name {
for alg_name in alg_names {
let src_view = src_image_data.view();
let mut dst_image = Image::new(new_width, new_height, src_view.pixel_type());
let mut dst_view = dst_image.view_mut();
let resize_alg = match alg_name {
"Nearest" => ResizeAlg::Nearest,
"Bilinear" => ResizeAlg::Convolution(FilterType::Bilinear),
"CatmullRom" => ResizeAlg::Convolution(FilterType::CatmullRom),
"Lanczos3" => ResizeAlg::Convolution(FilterType::Lanczos3),
_ => return,
};
let mut fast_resizer = Resizer::new(resize_alg);
unsafe {
fast_resizer.reset_internal_buffers();
fast_resizer.set_cpu_extensions(cpu_ext);
}
bench.task(format!("fir {} - {}", ext_name, alg_name), |task| {
task.iter(|| {
fast_resizer.resize(&src_view, &mut dst_view).unwrap();
})
});
}
}
utils::print_md_table(bench);
pub fn bench_downscale_rgb16(bench_group: &mut utils::BenchGroup) {
type P = U16x3;
let src_image = P::load_big_image().to_rgb16();
utils::image_resize(bench_group, &src_image);
utils::resize_resize(
bench_group,
RGB16,
src_image.as_raw().as_rgb(),
src_image.width(),
src_image.height(),
);
utils::fir_resize::<P>(bench_group);
}
bench_main!("Compare resize of RGB16 image", bench_downscale_rgb16,);
fn main() {
let res = utils::run_bench(bench_downscale_rgb16, "Compare resize of RGB16 image");
utils::print_and_write_compare_result(&res);
}
+15 -107
View File
@@ -1,117 +1,25 @@
use std::num::NonZeroU32;
use std::thread::sleep;
use std::time::Duration;
use glassbench::*;
use resize::px::RGBA;
use resize::Pixel::RGBA8P;
use rgb::FromSlice;
use fast_image_resize::pixels::U8x4;
use fast_image_resize::{CpuExtensions, FilterType, Image, MulDiv, ResizeAlg, Resizer};
use testing::PixelTestingExt;
mod utils;
pub fn bench_downscale_rgba(bench: &mut Bench) {
let src_image = U8x4::load_big_image().to_rgba8();
let new_width = NonZeroU32::new(852).unwrap();
let new_height = NonZeroU32::new(567).unwrap();
let alg_names = ["Nearest", "Bilinear", "CatmullRom", "Lanczos3"];
// resize crate
// https://crates.io/crates/resize
for alg_name in alg_names {
let resize_src_image = src_image.as_raw().as_rgba();
let mut dst = vec![RGBA::new(0, 0, 0, 0); (new_width.get() * new_height.get()) as usize];
bench.task(format!("resize - {}", alg_name), |task| {
let filter = match alg_name {
"Nearest" => {
// resizer doesn't support "nearest" algorithm
task.iter(|| sleep(Duration::new(0, 1)));
return;
}
"Bilinear" => resize::Type::Triangle,
"CatmullRom" => resize::Type::Catrom,
"Lanczos3" => resize::Type::Lanczos3,
_ => return,
};
let mut resize = resize::new(
src_image.width() as usize,
src_image.height() as usize,
new_width.get() as usize,
new_height.get() as usize,
RGBA8P,
filter,
)
.unwrap();
task.iter(|| {
resize.resize(resize_src_image, &mut dst).unwrap();
})
});
}
// fast_image_resize crate;
let src_image_data = U8x4::load_big_src_image();
let mut cpu_ext_and_name = vec![(CpuExtensions::None, "rust")];
#[cfg(target_arch = "x86_64")]
{
cpu_ext_and_name.push((CpuExtensions::Sse4_1, "sse4.1"));
cpu_ext_and_name.push((CpuExtensions::Avx2, "avx2"));
}
#[cfg(target_arch = "aarch64")]
{
cpu_ext_and_name.push((CpuExtensions::Neon, "neon"));
}
for (cpu_ext, ext_name) in cpu_ext_and_name {
for alg_name in alg_names {
let resize_alg = match alg_name {
"Nearest" => ResizeAlg::Nearest,
"Bilinear" => ResizeAlg::Convolution(FilterType::Bilinear),
"CatmullRom" => ResizeAlg::Convolution(FilterType::CatmullRom),
"Lanczos3" => ResizeAlg::Convolution(FilterType::Lanczos3),
_ => return,
};
let src_view = src_image_data.view();
let mut premultiplied_src_image = Image::new(
NonZeroU32::new(src_image.width()).unwrap(),
NonZeroU32::new(src_image.height()).unwrap(),
src_view.pixel_type(),
);
let mut dst_image = Image::new(new_width, new_height, src_view.pixel_type());
let mut dst_view = dst_image.view_mut();
let mut mul_div = MulDiv::default();
let mut fast_resizer = Resizer::new(resize_alg);
unsafe {
fast_resizer.reset_internal_buffers();
fast_resizer.set_cpu_extensions(cpu_ext);
mul_div.set_cpu_extensions(cpu_ext);
}
bench.task(format!("fir {} - {}", ext_name, alg_name), |task| {
task.iter(|| match resize_alg {
ResizeAlg::Nearest => {
fast_resizer.resize(&src_view, &mut dst_view).unwrap();
}
_ => {
let mut premultiplied_view = premultiplied_src_image.view_mut();
mul_div
.multiply_alpha(&src_view, &mut premultiplied_view)
.unwrap();
fast_resizer
.resize(&premultiplied_view.into(), &mut dst_view)
.unwrap();
mul_div.divide_alpha_inplace(&mut dst_view).unwrap();
}
})
});
}
}
utils::print_md_table(bench);
pub fn bench_downscale_rgba(bench_group: &mut utils::BenchGroup) {
type P = U8x4;
let src_image = P::load_big_image().to_rgba8();
utils::resize_resize(
bench_group,
RGBA8P,
src_image.as_raw().as_rgba(),
src_image.width(),
src_image.height(),
);
utils::fir_resize_with_alpha::<P>(bench_group);
}
bench_main!("Compare resize of RGBA image", bench_downscale_rgba,);
fn main() {
let res = utils::run_bench(bench_downscale_rgba, "Compare resize of RGBA image");
utils::print_and_write_compare_result(&res);
}
+15 -112
View File
@@ -1,122 +1,25 @@
use glassbench::*;
use resize::px::RGBA;
use resize::Pixel::RGBA16P;
use rgb::FromSlice;
use std::num::NonZeroU32;
use std::thread::sleep;
use std::time::Duration;
use fast_image_resize::pixels::U16x4;
use fast_image_resize::{CpuExtensions, FilterType, Image, MulDiv, ResizeAlg, Resizer};
use testing::PixelTestingExt;
mod utils;
pub fn bench_downscale_rgba16(bench: &mut Bench) {
let new_width = NonZeroU32::new(852).unwrap();
let new_height = NonZeroU32::new(567).unwrap();
let alg_names = ["Nearest", "Bilinear", "CatmullRom", "Lanczos3"];
// resize crate
// https://crates.io/crates/resize
let src_image = U16x4::load_big_image().to_rgba16();
for alg_name in alg_names {
let resize_src_image = src_image.as_raw().as_rgba();
let mut dst =
vec![RGBA::new(0u16, 0u16, 0u16, 0u16); (new_width.get() * new_height.get()) as usize];
bench.task(format!("resize - {}", alg_name), |task| {
let filter = match alg_name {
"Nearest" => {
// resizer doesn't support "nearest" algorithm
task.iter(|| sleep(Duration::new(0, 1)));
return;
}
"Bilinear" => resize::Type::Triangle,
"CatmullRom" => resize::Type::Catrom,
"Lanczos3" => resize::Type::Lanczos3,
_ => return,
};
let mut resize = resize::new(
src_image.width() as usize,
src_image.height() as usize,
new_width.get() as usize,
new_height.get() as usize,
RGBA16P,
filter,
)
.unwrap();
task.iter(|| {
resize.resize(resize_src_image, &mut dst).unwrap();
})
});
}
// fast_image_resize crate;
let src_image_data = U16x4::load_big_src_image();
let mut cpu_ext_and_name = vec![(CpuExtensions::None, "rust")];
#[cfg(target_arch = "x86_64")]
{
cpu_ext_and_name.push((CpuExtensions::Sse4_1, "sse4.1"));
cpu_ext_and_name.push((CpuExtensions::Avx2, "avx2"));
}
#[cfg(target_arch = "aarch64")]
{
cpu_ext_and_name.push((CpuExtensions::Neon, "neon"));
}
for (cpu_ext, ext_name) in cpu_ext_and_name {
for alg_name in alg_names {
let resize_alg = match alg_name {
"Nearest" => ResizeAlg::Nearest,
"Bilinear" => ResizeAlg::Convolution(FilterType::Bilinear),
"CatmullRom" => ResizeAlg::Convolution(FilterType::CatmullRom),
"Lanczos3" => ResizeAlg::Convolution(FilterType::Lanczos3),
_ => return,
};
let src_view = src_image_data.view();
let mut premultiplied_src_image = Image::new(
NonZeroU32::new(src_image.width()).unwrap(),
NonZeroU32::new(src_image.height()).unwrap(),
src_view.pixel_type(),
);
let mut dst_image = Image::new(new_width, new_height, src_view.pixel_type());
let mut dst_view = dst_image.view_mut();
let mut mul_div = MulDiv::default();
let mut fast_resizer = Resizer::new(resize_alg);
unsafe {
fast_resizer.reset_internal_buffers();
fast_resizer.set_cpu_extensions(cpu_ext);
mul_div.set_cpu_extensions(cpu_ext);
}
bench.task(
format!("fir {} - {}", ext_name, alg_name),
|task| match resize_alg {
ResizeAlg::Nearest => {
task.iter(|| {
fast_resizer.resize(&src_view, &mut dst_view).unwrap();
});
}
_ => {
task.iter(|| {
let mut premultiplied_view = premultiplied_src_image.view_mut();
mul_div
.multiply_alpha(&src_view, &mut premultiplied_view)
.unwrap();
fast_resizer
.resize(&premultiplied_view.into(), &mut dst_view)
.unwrap();
mul_div.divide_alpha_inplace(&mut dst_view).unwrap();
});
}
},
);
}
}
utils::print_md_table(bench);
pub fn bench_downscale_rgba16(bench_group: &mut utils::BenchGroup) {
type P = U16x4;
let src_image = P::load_big_image().to_rgba16();
utils::resize_resize(
bench_group,
RGBA16P,
src_image.as_raw().as_rgba(),
src_image.width(),
src_image.height(),
);
utils::fir_resize_with_alpha::<P>(bench_group);
}
bench_main!("Compare resize of RGBA16 image", bench_downscale_rgba16,);
fn main() {
let res = utils::run_bench(bench_downscale_rgba16, "Compare resize of RGBA16 image");
utils::print_and_write_compare_result(&res);
}
+54 -70
View File
@@ -1,7 +1,5 @@
use std::num::NonZeroU32;
use glassbench::*;
use fast_image_resize::pixels::*;
use fast_image_resize::Image;
use fast_image_resize::{CpuExtensions, FilterType, PixelType, ResizeAlg, Resizer};
@@ -12,7 +10,7 @@ mod utils;
const NEW_WIDTH: u32 = 852;
const NEW_HEIGHT: u32 = 567;
fn native_nearest_u8x4_bench(bench: &mut Bench) {
fn native_nearest_u8x4_bench(bench_group: &mut utils::BenchGroup) {
let image = U8x4::load_big_src_image();
let mut res_image = Image::new(
NonZeroU32::new(NEW_WIDTH).unwrap(),
@@ -25,15 +23,15 @@ fn native_nearest_u8x4_bench(bench: &mut Bench) {
unsafe {
resizer.set_cpu_extensions(CpuExtensions::None);
}
bench.task("nearest wo SIMD", |task| {
task.iter(|| {
bench_group.bench_function("nearest wo SIMD", |bencher| {
bencher.iter(|| {
resizer.resize(&src_image, &mut dst_image).unwrap();
})
});
}
fn downscale_bench(
bench: &mut Bench,
bench_group: &mut utils::BenchGroup,
image: &Image<'static>,
cpu_extensions: CpuExtensions,
filter_type: FilterType,
@@ -55,14 +53,14 @@ fn downscale_bench(
filter_type,
cpu_ext_into_str(cpu_extensions),
);
bench.task(bench_name, |task| {
task.iter(|| {
bench_group.bench_function(bench_name, |bencher| {
bencher.iter(|| {
resizer.resize(&src_image, &mut dst_image).unwrap();
})
});
}
fn native_nearest_u8_bench(bench: &mut Bench) {
fn native_nearest_u8_bench(bench_group: &mut utils::BenchGroup) {
let image = U8::load_big_src_image();
let mut res_image = Image::new(
NonZeroU32::new(NEW_WIDTH).unwrap(),
@@ -75,71 +73,57 @@ fn native_nearest_u8_bench(bench: &mut Bench) {
unsafe {
resizer.set_cpu_extensions(CpuExtensions::None);
}
bench.task("u8 nearest wo SIMD", |task| {
task.iter(|| {
bench_group.bench_function("u8 nearest wo SIMD", |bencher| {
bencher.iter(|| {
resizer.resize(&src_image, &mut dst_image).unwrap();
})
});
}
pub fn main() {
// Pin process to #0 CPU core
let mut cpu_set = nix::sched::CpuSet::new();
cpu_set.set(0).unwrap();
nix::sched::sched_setaffinity(nix::unistd::Pid::from_raw(0), &cpu_set).unwrap();
use glassbench::*;
let name = env!("CARGO_CRATE_NAME");
let cmd = Command::read();
if cmd.include_bench(name) {
let mut bench = create_bench(name, "Resize", &cmd);
let pixel_types = [
PixelType::U8,
PixelType::U8x2,
PixelType::U8x3,
PixelType::U8x4,
PixelType::U16,
PixelType::U16x2,
PixelType::U16x3,
PixelType::U16x4,
PixelType::I32,
];
let mut cpu_extensions = vec![CpuExtensions::None];
#[cfg(target_arch = "x86_64")]
{
cpu_extensions.push(CpuExtensions::Sse4_1);
cpu_extensions.push(CpuExtensions::Avx2);
}
#[cfg(target_arch = "aarch64")]
{
cpu_extensions.push(CpuExtensions::Neon);
}
for pixel_type in pixel_types {
for &cpu_extension in cpu_extensions.iter() {
let image = match pixel_type {
PixelType::U8 => U8::load_big_src_image(),
PixelType::U8x2 => U8x2::load_big_src_image(),
PixelType::U8x3 => U8x3::load_big_src_image(),
PixelType::U8x4 => U8x4::load_big_src_image(),
PixelType::U16 => U16::load_big_src_image(),
PixelType::U16x2 => U16x2::load_big_src_image(),
PixelType::U16x3 => U16x3::load_big_src_image(),
PixelType::U16x4 => U16x4::load_big_src_image(),
PixelType::I32 => I32::load_big_src_image(),
_ => unreachable!(),
};
downscale_bench(&mut bench, &image, cpu_extension, FilterType::Lanczos3);
}
}
native_nearest_u8x4_bench(&mut bench);
native_nearest_u8_bench(&mut bench);
if let Err(e) = after_bench(&mut bench, &cmd) {
eprintln!("{:?}", e);
}
} else {
println!("skipping bench {:?}", &name);
pub fn resize_bench(bench_group: &mut utils::BenchGroup) {
let pixel_types = [
PixelType::U8,
PixelType::U8x2,
PixelType::U8x3,
PixelType::U8x4,
PixelType::U16,
PixelType::U16x2,
PixelType::U16x3,
PixelType::U16x4,
PixelType::I32,
];
let mut cpu_extensions = vec![CpuExtensions::None];
#[cfg(target_arch = "x86_64")]
{
cpu_extensions.push(CpuExtensions::Sse4_1);
cpu_extensions.push(CpuExtensions::Avx2);
}
#[cfg(target_arch = "aarch64")]
{
cpu_extensions.push(CpuExtensions::Neon);
}
for pixel_type in pixel_types {
for &cpu_extension in cpu_extensions.iter() {
let image = match pixel_type {
PixelType::U8 => U8::load_big_src_image(),
PixelType::U8x2 => U8x2::load_big_src_image(),
PixelType::U8x3 => U8x3::load_big_src_image(),
PixelType::U8x4 => U8x4::load_big_src_image(),
PixelType::U16 => U16::load_big_src_image(),
PixelType::U16x2 => U16x2::load_big_src_image(),
PixelType::U16x3 => U16x3::load_big_src_image(),
PixelType::U16x4 => U16x4::load_big_src_image(),
PixelType::I32 => I32::load_big_src_image(),
_ => unreachable!(),
};
downscale_bench(bench_group, &image, cpu_extension, FilterType::Lanczos3);
}
}
native_nearest_u8x4_bench(bench_group);
native_nearest_u8_bench(bench_group);
}
fn main() {
utils::run_bench(resize_bench, "Resize");
}
+73
View File
@@ -0,0 +1,73 @@
use std::env;
use std::path::PathBuf;
use std::time::SystemTime;
use criterion::measurement::WallTime;
use criterion::{Bencher, BenchmarkGroup, BenchmarkId, Criterion};
use super::{cargo_target_directory, get_arch_name, get_results, BenchResult};
pub type BenchGroup<'a> = BenchmarkGroup<'a, WallTime>;
pub fn run_bench<F>(bench_fn: F, name: &str) -> Vec<BenchResult>
where
F: FnOnce(&mut BenchGroup),
{
pin_process_to_cpu0();
let arch_name = get_arch_name();
let output_dir = criterion_output_directory().join(arch_name);
let mut criterion = Criterion::default()
.output_directory(&output_dir)
.configure_from_args();
let now = SystemTime::now();
let mut group = criterion.benchmark_group(name);
bench_fn(&mut group);
group.finish();
criterion.final_summary();
let results_dir = output_dir.join(name);
get_results(&results_dir, &now)
}
pub fn bench<S1, S2, F>(
group: &mut BenchGroup,
sample_size: usize,
func_name: S1,
parameter: S2,
mut f: F,
) where
S1: Into<String>,
S2: Into<String>,
F: FnMut(&mut Bencher),
{
let parameter = parameter.into();
group.sample_size(sample_size);
group.bench_with_input(
BenchmarkId::new(func_name, &parameter),
&parameter,
|bencher, _| f(bencher),
);
}
/// Pin process to #0 CPU core
pub fn pin_process_to_cpu0() {
#[cfg(not(target_arch = "wasm32"))]
{
let mut cpu_set = nix::sched::CpuSet::new();
cpu_set.set(0).unwrap();
nix::sched::sched_setaffinity(nix::unistd::Pid::from_raw(0), &cpu_set).unwrap();
}
}
fn criterion_output_directory() -> PathBuf {
if let Some(value) = env::var_os("CRITERION_HOME") {
PathBuf::from(value)
} else if let Some(path) = cargo_target_directory() {
path.join("criterion")
} else {
PathBuf::from("target/criterion")
}
}
+39 -106
View File
@@ -1,115 +1,48 @@
use std::collections::HashMap;
use std::env;
use std::path::PathBuf;
use std::process::Command;
use glassbench::*;
use serde::Deserialize;
pub fn print_md_table(bench: &Bench) {
let mut res_map: HashMap<String, Vec<String>> = HashMap::new();
let mut crate_names: Vec<String> = Vec::new();
let mut alg_names: Vec<String> = Vec::new();
pub use bencher::*;
pub use resize_functions::*;
pub use results::*;
for task in bench.tasks.iter() {
if let Some(measure) = task.measure {
let parts: Vec<&str> = task.name.split('-').map(|s| s.trim()).collect();
let crate_name = parts[0].to_string();
let alg_name = parts[1].to_string();
let value = measure.total_duration.as_secs_f64() * 1000. / measure.iterations as f64;
mod bencher;
mod resize_functions;
mod results;
if !crate_names.contains(&crate_name) {
crate_names.push(crate_name.clone());
}
if !alg_names.contains(&alg_name) {
alg_names.push(alg_name);
}
if !res_map.contains_key(&crate_name) {
res_map.insert(crate_name.clone(), Vec::new());
}
if let Some(values) = res_map.get_mut(&crate_name) {
if value < 0.10 {
values.push("-".to_string());
} else {
let s_value = format!("{:.2}", value);
values.push(s_value);
}
}
}
}
let first_column_width = res_map.keys().map(|s| s.len()).max().unwrap_or(0);
let mut column_width: Vec<usize> = vec![first_column_width];
for (i, name) in alg_names.iter().enumerate() {
let width = res_map.values().map(|v| v[i].len()).max().unwrap_or(0);
column_width.push(width.max(name.len()));
}
let mut first_row: Vec<String> = vec!["".to_owned()];
alg_names.iter().for_each(|s| first_row.push(s.to_owned()));
print_row(&column_width, &first_row);
print_header_underline(&column_width);
for name in crate_names.iter() {
if let Some(values) = res_map.get(name) {
let mut row = vec![name.clone()];
values.iter().for_each(|s| row.push(s.clone()));
print_row(&column_width, &row);
}
}
const fn get_arch_name() -> &'static str {
#[cfg(target_arch = "x86_64")]
return "x86_64";
#[cfg(target_arch = "aarch64")]
return "arm64";
#[cfg(target_arch = "wasm32")]
return "wasm32";
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "wasm32"
)))]
return "unknown";
}
fn print_row(widths: &[usize], values: &[String]) {
for (i, (&width, value)) in widths.iter().zip(values).enumerate() {
if i == 0 {
print!("| {:width$} ", value, width = width);
} else {
print!("| {:^width$} ", value, width = width);
}
/// Returns the Cargo target directory, possibly calling `cargo metadata` to
/// figure it out.
fn cargo_target_directory() -> Option<PathBuf> {
#[derive(Deserialize)]
struct Metadata {
target_directory: PathBuf,
}
println!("|");
}
fn print_header_underline(widths: &[usize]) {
for (i, &width) in widths.iter().enumerate() {
if i == 0 {
print!("|{:-<width$}", "", width = width + 2);
} else {
print!("|:{:-<width$}:", "", width = width);
}
}
println!("|");
}
/// Generates a benchmark with a consistent id
/// (using the benchmark file title), calling
/// the benchmarking functions given in argument.
///
/// ```no-test
/// bench_main!(
/// "Sortings",
/// bench_number_sorting,
/// bench_alpha_sorting,
/// );
/// ```
///
/// This generates the whole main function.
/// If you want to set the bench name yourself
/// (not recommanded), or change the way the launch
/// arguments are used, you can write the main
/// yourself and call [create_bench] and [after_bench]
/// instead of using this macro.
#[macro_export]
macro_rules! bench_main {
(
$title: literal,
$( $fun: path, )+
) => {
pub fn main() {
// Pin process to #0 CPU core
let mut cpu_set = nix::sched::CpuSet::new();
cpu_set.set(0).unwrap();
nix::sched::sched_setaffinity(nix::unistd::Pid::from_raw(0), &cpu_set).unwrap();
glassbench!($title, $($fun,)+);
main();
}
}
env::var_os("CARGO_TARGET_DIR")
.map(PathBuf::from)
.or_else(|| {
let output = Command::new(env::var_os("CARGO")?)
.args(["metadata", "--format-version", "1"])
.output()
.ok()?;
let metadata: Metadata = serde_json::from_slice(&output.stdout).ok()?;
Some(metadata.target_directory)
})
}
+212
View File
@@ -0,0 +1,212 @@
use std::ops::Deref;
use image::{imageops, ImageBuffer};
use crate::utils::bencher::{bench, BenchGroup};
use fast_image_resize::{CpuExtensions, FilterType, Image, MulDiv, ResizeAlg, Resizer};
use testing::{nonzero, PixelTestingExt};
const ALG_NAMES: [&str; 4] = ["Nearest", "Bilinear", "CatmullRom", "Lanczos3"];
const NEW_WIDTH: u32 = 852;
const NEW_HEIGHT: u32 = 567;
/// Resize image with help of "image" crate (https://crates.io/crates/image)
pub fn image_resize<P, C>(bench_group: &mut BenchGroup, src_image: &ImageBuffer<P, C>)
where
P: image::Pixel + 'static,
C: Deref<Target = [P::Subpixel]>,
{
for alg_name in ALG_NAMES {
let (filter, sample_size) = match alg_name {
"Nearest" => (imageops::Nearest, 80),
"Bilinear" => (imageops::Triangle, 50),
"CatmullRom" => (imageops::CatmullRom, 30),
"Lanczos3" => (imageops::Lanczos3, 20),
_ => continue,
};
bench(bench_group, sample_size, "image", alg_name, |bencher| {
bencher.iter(|| {
imageops::resize(src_image, NEW_WIDTH, NEW_HEIGHT, filter);
})
});
}
}
/// Resize image with help of "resize" crate (https://crates.io/crates/resize)
pub fn resize_resize<Format, Out>(
bench_group: &mut BenchGroup,
pixel_format: Format,
src_image: &[Format::InputPixel],
src_width: u32,
src_height: u32,
) where
Out: Clone,
Format: resize::PixelFormat<OutputPixel = Out> + Copy,
{
for alg_name in ALG_NAMES {
if alg_name == "Nearest" {
// "resize" doesn't support "nearest" algorithm
continue;
}
let mut dst =
vec![pixel_format.into_pixel(Format::new()); (NEW_WIDTH * NEW_HEIGHT) as usize];
let sample_size = if alg_name == "Lanczos3" { 60 } else { 100 };
bench(bench_group, sample_size, "resize", alg_name, |bencher| {
let filter = match alg_name {
"Bilinear" => resize::Type::Triangle,
"CatmullRom" => resize::Type::Catrom,
"Lanczos3" => resize::Type::Lanczos3,
_ => return,
};
let mut resizer = resize::new(
src_width as usize,
src_height as usize,
NEW_WIDTH as usize,
NEW_HEIGHT as usize,
pixel_format,
filter,
)
.unwrap();
bencher.iter(|| {
resizer.resize(src_image, &mut dst).unwrap();
})
});
}
}
/// Resize image with help of "fast_imager_resize" crate
pub fn fir_resize<P: PixelTestingExt>(bench_group: &mut BenchGroup) {
let src_image_data = P::load_big_src_image();
let src_view = src_image_data.view();
let mut dst_image = Image::new(
nonzero(NEW_WIDTH),
nonzero(NEW_HEIGHT),
src_view.pixel_type(),
);
let mut dst_view = dst_image.view_mut();
let mut cpu_ext_and_name = vec![(CpuExtensions::None, "rust")];
#[cfg(target_arch = "x86_64")]
{
cpu_ext_and_name.push((CpuExtensions::Sse4_1, "sse4.1"));
cpu_ext_and_name.push((CpuExtensions::Avx2, "avx2"));
}
#[cfg(target_arch = "aarch64")]
{
cpu_ext_and_name.push((CpuExtensions::Neon, "neon"));
}
for (cpu_ext, ext_name) in cpu_ext_and_name {
for alg_name in ALG_NAMES {
let resize_alg = match alg_name {
"Nearest" => {
if cpu_ext != CpuExtensions::None {
// Nearest algorithm implemented only for native Rust.
continue;
}
ResizeAlg::Nearest
}
"Bilinear" => ResizeAlg::Convolution(FilterType::Bilinear),
"CatmullRom" => ResizeAlg::Convolution(FilterType::CatmullRom),
"Lanczos3" => ResizeAlg::Convolution(FilterType::Lanczos3),
_ => continue,
};
let mut fast_resizer = Resizer::new(resize_alg);
unsafe {
fast_resizer.set_cpu_extensions(cpu_ext);
}
let sample_size = 100;
bench(
bench_group,
sample_size,
format!("fir {}", ext_name),
alg_name,
|bencher| {
fast_resizer.reset_internal_buffers();
bencher.iter(|| {
fast_resizer.resize(&src_view, &mut dst_view).unwrap();
})
},
);
}
}
}
/// Resize image with alpha channel with help of "fast_imager_resize" crate
pub fn fir_resize_with_alpha<P: PixelTestingExt>(bench_group: &mut BenchGroup) {
let src_image = P::load_big_src_image();
let src_view = src_image.view();
let mut premultiplied_src_image =
Image::new(src_image.width(), src_image.height(), src_view.pixel_type());
let mut dst_image = Image::new(
nonzero(NEW_WIDTH),
nonzero(NEW_HEIGHT),
src_view.pixel_type(),
);
let mut dst_view = dst_image.view_mut();
let mut mul_div = MulDiv::default();
let mut cpu_ext_and_name = vec![(CpuExtensions::None, "rust")];
#[cfg(target_arch = "x86_64")]
{
cpu_ext_and_name.push((CpuExtensions::Sse4_1, "sse4.1"));
cpu_ext_and_name.push((CpuExtensions::Avx2, "avx2"));
}
#[cfg(target_arch = "aarch64")]
{
cpu_ext_and_name.push((CpuExtensions::Neon, "neon"));
}
for (cpu_ext, ext_name) in cpu_ext_and_name {
for alg_name in ALG_NAMES {
let resize_alg = match alg_name {
"Nearest" => {
if cpu_ext != CpuExtensions::None {
// Nearest algorithm implemented only for native Rust.
continue;
}
ResizeAlg::Nearest
}
"Bilinear" => ResizeAlg::Convolution(FilterType::Bilinear),
"CatmullRom" => ResizeAlg::Convolution(FilterType::CatmullRom),
"Lanczos3" => ResizeAlg::Convolution(FilterType::Lanczos3),
_ => return,
};
let mut fast_resizer = Resizer::new(resize_alg);
unsafe {
fast_resizer.set_cpu_extensions(cpu_ext);
mul_div.set_cpu_extensions(cpu_ext);
}
let sample_size = 100;
bench(
bench_group,
sample_size,
format!("fir {}", ext_name),
alg_name,
|bencher| {
fast_resizer.reset_internal_buffers();
match resize_alg {
ResizeAlg::Nearest => {
bencher.iter(|| {
fast_resizer.resize(&src_view, &mut dst_view).unwrap();
});
}
_ => {
bencher.iter(|| {
let mut premultiplied_view = premultiplied_src_image.view_mut();
mul_div
.multiply_alpha(&src_view, &mut premultiplied_view)
.unwrap();
fast_resizer
.resize(&premultiplied_view.into(), &mut dst_view)
.unwrap();
mul_div.divide_alpha_inplace(&mut dst_view).unwrap();
});
}
}
},
);
}
}
}
+267
View File
@@ -0,0 +1,267 @@
use std::borrow::Cow;
use std::collections::HashMap;
use std::env;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
use itertools::Itertools;
use serde::Deserialize;
use walkdir::WalkDir;
use super::get_arch_name;
#[derive(Debug)]
pub struct BenchResult {
pub function_name: String,
pub parameter: String,
/// Estimate time in nanoseconds
pub estimate: f64,
}
impl BenchResult {
pub fn new(function_name: String, parameter: Option<String>, path: &Path) -> Self {
#[derive(Deserialize)]
struct Mean {
point_estimate: f64,
}
#[derive(Deserialize)]
struct Estimates {
mean: Mean,
}
let data =
std::fs::read_to_string(path).expect("Unable to read file with benchmark results");
let estimates: Estimates =
serde_json::from_str(&data).expect("Unable to parse JSON data with benchmark results");
Self {
function_name,
parameter: parameter.unwrap_or_default(),
estimate: estimates.mean.point_estimate,
}
}
}
/// Find all "new/estimates.json" files inside of given directory.
/// Get only files what were created after the given time.
/// Read estimate time from this files and return vector of `BenchResult` instances.
pub fn get_results(parent_dir: &PathBuf, modified_after: &SystemTime) -> Vec<BenchResult> {
let mut result = vec![];
if !parent_dir.is_dir() {
println!("WARNING: Directory with bench results is absent");
return result;
}
let result_paths = WalkDir::new(parent_dir)
.follow_links(true)
.into_iter()
.map(|e| e.expect("Invalid FS entry"))
.filter(|e| e.path().ends_with("new/estimates.json"))
.map(|e| (e.metadata().expect("Unable get metadata for FS entity"), e))
.filter(|(m, _)| m.is_file())
.map(|(m, e)| {
(
m.modified()
.expect("Unable to get last modification time of estimates.json file"),
e,
)
})
// Exclude old results
.filter(|(modified, _)| modified >= modified_after)
.sorted_by_key(|(modified, _)| modified.to_owned())
.map(|(_, e)| e.into_path());
for path in result_paths {
let rel_path = path.strip_prefix(parent_dir).unwrap_or(&path).to_path_buf();
let path_components: Vec<String> = rel_path
.iter()
.map(|os_str| {
os_str
.to_str()
.expect("Unable to convert FS entry name into String")
.to_string()
})
.collect();
let (function_name, parameter_name) = match path_components.as_slice() {
[f, p, _, _] => (f.to_string(), Some(p.to_string())),
[f, _, _] => (f.to_string(), None),
_ => panic!("Relative path to bench result is invalid"),
};
result.push(BenchResult::new(function_name, parameter_name, &path));
}
result
}
static COL_ORDER: [&str; 4] = ["Nearest", "Bilinear", "CatmullRom", "Lanczos3"];
pub fn build_md_table(bench_results: &[BenchResult]) -> String {
let mut row_names: Vec<String> = Vec::new();
let mut row_indexes: HashMap<String, usize> = HashMap::new();
let mut col_names: Vec<String> = Vec::new();
for result in bench_results {
let row_name = result.function_name.clone();
if !row_names.contains(&row_name) {
row_names.push(row_name.clone());
row_indexes.insert(row_name.clone(), row_names.len() - 1);
}
let col_name = result.parameter.clone();
if !col_names.contains(&col_name) {
col_names.push(col_name.clone());
}
}
// Reorder columns
let mut ordered_pos = 0;
for name in COL_ORDER {
if let Some((cur_pos, _)) = col_names.iter().find_position(|s| s.as_str() == name) {
if cur_pos != ordered_pos {
col_names.swap(cur_pos, ordered_pos);
}
ordered_pos += 1;
}
}
let col_indexes: HashMap<String, usize> = col_names
.iter()
.enumerate()
.map(|(i, v)| (v.clone(), i))
.collect();
let cols_count = col_names.len();
let mut values = vec![Cow::Borrowed("-"); row_names.len() * cols_count];
for result in bench_results {
let row_index = row_indexes.get(&result.function_name).copied();
let col_index = col_indexes.get(&result.parameter).copied();
if let (Some(row_index), Some(col_index)) = (row_index, col_index) {
let value = result.estimate / 1000000.;
if value >= 0.10 {
let value_index = row_index * cols_count + col_index;
values[value_index] = Cow::Owned(format!("{:.2}", value));
}
}
}
let first_column_width = row_names.iter().map(|s| s.len()).max().unwrap_or(0);
let mut column_width: Vec<usize> = vec![first_column_width];
for (col_index, col_name) in col_names.iter().enumerate() {
let width = (0..row_names.len())
.map(|row_index| {
let value_index = row_index * cols_count + col_index;
values.get(value_index).map(|v| v.len()).unwrap_or(0)
})
.max()
.unwrap_or(0);
column_width.push(width.max(col_name.len()));
}
let mut first_row: Vec<String> = vec!["".to_owned()];
col_names.iter().for_each(|s| first_row.push(s.to_owned()));
let mut str_buffer: Vec<String> = vec![];
table_row(&mut str_buffer, &column_width, &first_row);
table_header_underline(&mut str_buffer, &column_width);
for row_name in row_names.iter() {
let mut row = vec![row_name.clone()];
for col_name in col_names.iter() {
let row_index = row_indexes.get(row_name).copied();
let col_index = col_indexes.get(col_name).copied();
if let (Some(row_index), Some(col_index)) = (row_index, col_index) {
let value_index = row_index * cols_count + col_index;
let value = values
.get(value_index)
.map(|v| v.to_string())
.unwrap_or_default();
row.push(value);
}
}
table_row(&mut str_buffer, &column_width, &row);
}
str_buffer.join("")
}
fn table_row(buffer: &mut Vec<String>, widths: &[usize], values: &[String]) {
for (i, (&width, value)) in widths.iter().zip(values).enumerate() {
if i == 0 {
buffer.push(format!("| {:width$} ", value, width = width));
} else {
buffer.push(format!("| {:^width$} ", value, width = width));
}
}
buffer.push("|\n".to_string());
}
fn table_header_underline(buffer: &mut Vec<String>, widths: &[usize]) {
for (i, &width) in widths.iter().enumerate() {
if i == 0 {
buffer.push(format!("|{:-<width$}", "", width = width + 2));
} else {
buffer.push(format!("|:{:-<width$}:", "", width = width));
}
}
buffer.push("|\n".to_string());
}
fn insert_string_into_file(path: &Path, placeholder_name: &str, string: &str) {
let mut content = std::fs::read_to_string(path).expect("Unable to read file into string");
let start_maker = format!("[comment]: <> ({} start)\n", placeholder_name);
let start = match content.find(&start_maker) {
Some(s) => s,
None => {
println!(
"WARNING: Can't find start marker for placeholder '{}' in file {:?}",
placeholder_name, path
);
return;
}
};
let end_maker = format!("[comment]: <> ({} end)", placeholder_name);
let end = match content.find(&end_maker) {
Some(s) => s,
None => {
println!(
"WARNING: Can't find end marker for placeholder '{}' in file {:?}",
placeholder_name, path
);
return;
}
};
let replace_str = [start_maker.as_str(), string].join("");
content.replace_range(start..end, &replace_str);
std::fs::write(path, content).expect("Unable to save string into file");
}
fn write_bench_results_into_file(md_table: &str) {
let file_name = format!("benchmarks-{}.md", get_arch_name());
let file_path = PathBuf::from(file_name);
if !file_path.is_file() {
panic!("Can't find file {:?} in current directory", file_path);
}
let crate_name = env!("CARGO_CRATE_NAME");
insert_string_into_file(file_path.as_path(), crate_name, md_table);
if get_arch_name() == "x86_64" {
let file_path = PathBuf::from("README.md");
if !file_path.is_file() {
panic!("Can't find file {:?} in current directory", file_path);
}
insert_string_into_file(file_path.as_path(), crate_name, md_table);
}
}
pub fn print_and_write_compare_result(bench_results: &[BenchResult]) {
if !bench_results.is_empty() {
let md_table = build_md_table(bench_results);
println!("{}", md_table);
if env::var("WRITE_COMPARE_RESULT").unwrap_or_else(|_| "".to_owned()) == "1" {
write_bench_results_into_file(&md_table);
}
}
}
+20 -4
View File
@@ -1,12 +1,12 @@
## Benchmarks of fast_image_resize crate
## Benchmarks of fast_image_resize crate for arm64 architecture
Environment:
- CPU: Neoverse-N1 2GHz (Oracle Cloud Compute, VM.Standard.A1.Flex)
- Ubuntu 22.04 (linux 5.15.0)
- Rust 1.65
- Rust 1.66.1
- criterion = "0.4"
- fast_image_resize = "2.4.0"
- glassbench = "0.3.3"
Other Rust libraries used to compare of resizing speed:
@@ -29,12 +29,14 @@ Pipeline:
- Source image [nasa-4928x3279.png](https://github.com/Cykooz/fast_image_resize/blob/main/data/nasa-4928x3279.png)
- Numbers in table is mean duration of image resizing in milliseconds.
[comment]: <> (bench_compare_rgb start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|----------|:-------:|:--------:|:----------:|:--------:|
| image | 84.33 | 170.75 | 319.95 | 448.10 |
| resize | - | 89.86 | 179.23 | 265.31 |
| fir rust | 0.90 | 71.68 | 87.07 | 112.47 |
| fir neon | 0.90 | 42.73 | 57.20 | 81.35 |
[comment]: <> (bench_compare_rgb end)
### Resize RGBA8 image (U8x4) 4928x3279 => 852x567
@@ -45,13 +47,15 @@ Pipeline:
- Source image
[nasa-4928x3279-rgba.png](https://github.com/Cykooz/fast_image_resize/blob/main/data/nasa-4928x3279-rgba.png)
- Numbers in table is mean duration of image resizing in milliseconds.
- The `image` crate does not support multiplying and dividing by alpha channel.
- The `image` crate does not support multiplying and dividing by alpha channel.
[comment]: <> (bench_compare_rgba start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|----------|:-------:|:--------:|:----------:|:--------:|
| resize | - | 99.18 | 181.80 | 276.46 |
| fir rust | 0.97 | 92.81 | 104.67 | 153.96 |
| fir neon | 0.97 | 44.09 | 61.19 | 82.98 |
[comment]: <> (bench_compare_rgba end)
### Resize L8 image (U8) 4928x3279 => 852x567
@@ -63,12 +67,14 @@ Pipeline:
has converted into grayscale image with one byte per pixel.
- Numbers in table is mean duration of image resizing in milliseconds.
[comment]: <> (bench_compare_l start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|----------|:-------:|:--------:|:----------:|:--------:|
| image | 74.86 | 105.85 | 175.70 | 243.32 |
| resize | - | 37.14 | 83.02 | 127.44 |
| fir rust | 0.51 | 29.42 | 37.32 | 45.47 |
| fir neon | 0.51 | 15.00 | 19.95 | 28.16 |
[comment]: <> (bench_compare_l end)
### Resize LA8 image (U8x2) 4928x3279 => 852x567
@@ -83,10 +89,12 @@ Pipeline:
- The `image` crate does not support multiplying and dividing by alpha channel.
- The `resize` crate does not support this pixel format.
[comment]: <> (bench_compare_la start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|----------|:-------:|:--------:|:----------:|:--------:|
| fir rust | 0.64 | 59.82 | 74.89 | 73.10 |
| fir neon | 0.64 | 34.80 | 39.24 | 54.70 |
[comment]: <> (bench_compare_la end)
### Resize RGB16 image (U16x3) 4928x3279 => 852x567
@@ -98,12 +106,14 @@ Pipeline:
has converted into RGB16 image.
- Numbers in table is mean duration of image resizing in milliseconds.
[comment]: <> (bench_compare_rgb16 start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|----------|:-------:|:--------:|:----------:|:--------:|
| image | 87.37 | 168.47 | 327.72 | 473.01 |
| resize | - | 91.11 | 182.73 | 271.48 |
| fir rust | 1.39 | 146.88 | 274.83 | 392.52 |
| fir neon | 1.39 | 70.00 | 92.13 | 128.36 |
[comment]: <> (bench_compare_rgb16 end)
### Resize RGBA16 image (U16x4) 4928x3279 => 852x567
@@ -116,11 +126,13 @@ Pipeline:
- Numbers in table is mean duration of image resizing in milliseconds.
- The `image` crate does not support multiplying and dividing by alpha channel.
[comment]: <> (bench_compare_rgba16 start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|----------|:-------:|:--------:|:----------:|:--------:|
| resize | - | 102.42 | 185.36 | 284.94 |
| fir rust | 1.51 | 202.31 | 365.93 | 521.22 |
| fir neon | 1.51 | 76.89 | 117.58 | 159.40 |
[comment]: <> (bench_compare_rgba16 end)
### Resize L16 image (U16) 4928x3279 => 852x567
@@ -132,12 +144,14 @@ Pipeline:
has converted into grayscale image with two bytes per pixel.
- Numbers in table is mean duration of image resizing in milliseconds.
[comment]: <> (bench_compare_l16 start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|----------|:-------:|:--------:|:----------:|:--------:|
| image | 76.09 | 110.82 | 186.44 | 257.87 |
| resize | - | 39.65 | 63.88 | 87.06 |
| fir rust | 0.63 | 57.32 | 94.92 | 134.83 |
| fir neon | 0.63 | 17.98 | 26.84 | 37.71 |
[comment]: <> (bench_compare_l16 end)
### Resize LA16 (luma with alpha channel) image (U16x2) 4928x3279 => 852x567
@@ -152,7 +166,9 @@ Pipeline:
- The `image` crate does not support multiplying and dividing by alpha channel.
- The `resize` crate does not support this pixel format.
[comment]: <> (bench_compare_la16 start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|----------|:-------:|:--------:|:----------:|:--------:|
| fir rust | 0.95 | 105.92 | 192.14 | 270.89 |
| fir neon | 0.95 | 35.57 | 53.51 | 73.13 |
[comment]: <> (bench_compare_la16 end)
+168
View File
@@ -0,0 +1,168 @@
## Benchmarks of fast_image_resize crate for Wasm32 architecture
Environment:
- CPU: AMD Ryzen 9 5950X
- RAM: DDR4 3800 MHz
- Ubuntu 22.04 (linux 5.15.0)
- Rust 1.66.1
- wasmtime = "4.0.0"
- criterion = "0.4"
- fast_image_resize = "2.4.0"
Other Rust libraries used to compare of resizing speed:
- image = "0.24.5" (<https://crates.io/crates/image>)
- resize = "0.7.4" (<https://crates.io/crates/resize>)
Resize algorithms:
- Nearest
- Convolution with Bilinear filter
- Convolution with CatmullRom filter
- Convolution with Lanczos3 filter
### Resize RGB8 image (U8x3) 4928x3279 => 852x567
Pipeline:
`src_image => resize => dst_image`
- Source image [nasa-4928x3279.png](https://github.com/Cykooz/fast_image_resize/blob/main/data/nasa-4928x3279.png)
- Numbers in table is mean duration of image resizing in milliseconds.
[comment]: <> (bench_compare_rgb start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|----------|:-------:|:--------:|:----------:|:--------:|
| image | 63.39 | 243.16 | 434.54 | 652.17 |
| resize | - | 109.40 | 202.84 | 295.71 |
| fir rust | 0.69 | 82.65 | 141.72 | 202.39 |
[comment]: <> (bench_compare_rgb end)
### Resize RGBA8 image (U8x4) 4928x3279 => 852x567
Pipeline:
`src_image => multiply by alpha => resize => divide by alpha => dst_image`
- Source image
[nasa-4928x3279-rgba.png](https://github.com/Cykooz/fast_image_resize/blob/main/data/nasa-4928x3279-rgba.png)
- Numbers in table is mean duration of image resizing in milliseconds.
- The `image` crate does not support multiplying and dividing by alpha channel.
[comment]: <> (bench_compare_rgba start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|----------|:-------:|:--------:|:----------:|:--------:|
| resize | - | 122.06 | 229.20 | 335.95 |
| fir rust | 0.69 | 161.38 | 255.27 | 351.55 |
[comment]: <> (bench_compare_rgba end)
### Resize L8 image (U8) 4928x3279 => 852x567
Pipeline:
`src_image => resize => dst_image`
- Source image [nasa-4928x3279.png](https://github.com/Cykooz/fast_image_resize/blob/main/data/nasa-4928x3279.png)
has converted into grayscale image with one byte per pixel.
- Numbers in table is mean duration of image resizing in milliseconds.
[comment]: <> (bench_compare_l start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|----------|:-------:|:--------:|:----------:|:--------:|
| image | 56.44 | 198.93 | 373.78 | 530.45 |
| resize | - | 46.04 | 77.65 | 112.78 |
| fir rust | 0.32 | 37.53 | 60.74 | 84.91 |
[comment]: <> (bench_compare_l end)
### Resize LA8 image (U8x2) 4928x3279 => 852x567
Pipeline:
`src_image => multiply by alpha => resize => divide by alpha => dst_image`
- Source image
[nasa-4928x3279-rgba.png](https://github.com/Cykooz/fast_image_resize/blob/main/data/nasa-4928x3279-rgba.png)
has converted into grayscale image with alpha channel (two bytes per pixel).
- Numbers in table is mean duration of image resizing in milliseconds.
- The `image` crate does not support multiplying and dividing by alpha channel.
- The `resize` crate does not support this pixel format.
[comment]: <> (bench_compare_la start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|----------|:-------:|:--------:|:----------:|:--------:|
| fir rust | 0.43 | 85.92 | 130.47 | 176.58 |
[comment]: <> (bench_compare_la end)
### Resize RGB16 image (U16x3) 4928x3279 => 852x567
Pipeline:
`src_image => resize => dst_image`
- Source image [nasa-4928x3279.png](https://github.com/Cykooz/fast_image_resize/blob/main/data/nasa-4928x3279.png)
has converted into RGB16 image.
- Numbers in table is mean duration of image resizing in milliseconds.
[comment]: <> (bench_compare_rgb16 start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|----------|:-------:|:--------:|:----------:|:--------:|
| image | 63.46 | 250.44 | 444.97 | 655.95 |
| resize | - | 94.81 | 174.13 | 253.26 |
| fir rust | 1.05 | 99.97 | 169.38 | 238.44 |
[comment]: <> (bench_compare_rgb16 end)
### Resize RGBA16 image (U16x4) 4928x3279 => 852x567
Pipeline:
`src_image => multiply by alpha => resize => divide by alpha => dst_image`
- Source image
[nasa-4928x3279-rgba.png](https://github.com/Cykooz/fast_image_resize/blob/main/data/nasa-4928x3279-rgba.png)
- Numbers in table is mean duration of image resizing in milliseconds.
- The `image` crate does not support multiplying and dividing by alpha channel.
[comment]: <> (bench_compare_rgba16 start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|----------|:-------:|:--------:|:----------:|:--------:|
| resize | - | 119.79 | 222.30 | 324.49 |
| fir rust | 1.22 | 166.55 | 247.73 | 334.47 |
[comment]: <> (bench_compare_rgba16 end)
### Resize L16 image (U16) 4928x3279 => 852x567
Pipeline:
`src_image => resize => dst_image`
- Source image [nasa-4928x3279.png](https://github.com/Cykooz/fast_image_resize/blob/main/data/nasa-4928x3279.png)
has converted into grayscale image with two bytes per pixel.
- Numbers in table is mean duration of image resizing in milliseconds.
[comment]: <> (bench_compare_l16 start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|----------|:-------:|:--------:|:----------:|:--------:|
| image | 55.75 | 214.72 | 412.35 | 586.57 |
| resize | - | 46.44 | 78.74 | 113.61 |
| fir rust | 0.42 | 44.82 | 71.45 | 99.62 |
[comment]: <> (bench_compare_l16 end)
### Resize LA16 (luma with alpha channel) image (U16x2) 4928x3279 => 852x567
Pipeline:
`src_image => multiply by alpha => resize => divide by alpha => dst_image`
- Source image
[nasa-4928x3279-rgba.png](https://github.com/Cykooz/fast_image_resize/blob/main/data/nasa-4928x3279-rgba.png)
has converted into grayscale image with alpha channel (four bytes per pixel).
- Numbers in table is mean duration of image resizing in milliseconds.
- The `image` crate does not support multiplying and dividing by alpha channel.
- The `resize` crate does not support this pixel format.
[comment]: <> (bench_compare_la16 start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|----------|:-------:|:--------:|:----------:|:--------:|
| fir rust | 0.70 | 98.81 | 149.67 | 201.25 |
[comment]: <> (bench_compare_la16 end)
+19 -3
View File
@@ -1,13 +1,13 @@
## Benchmarks of fast_image_resize crate
## Benchmarks of fast_image_resize crate for x86_64 architecture
Environment:
- CPU: AMD Ryzen 9 5950X
- RAM: DDR4 3800 MHz
- Ubuntu 22.04 (linux 5.15.0)
- Rust 1.65
- Rust 1.66.1
- criterion = "0.4"
- fast_image_resize = "2.4.0"
- glassbench = "0.3.3"
Other Rust libraries used to compare of resizing speed:
@@ -30,6 +30,7 @@ Pipeline:
- Source image [nasa-4928x3279.png](https://github.com/Cykooz/fast_image_resize/blob/main/data/nasa-4928x3279.png)
- Numbers in table is mean duration of image resizing in milliseconds.
[comment]: <> (bench_compare_rgb start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|------------|:-------:|:--------:|:----------:|:--------:|
| image | 20.66 | 82.74 | 141.56 | 199.96 |
@@ -37,6 +38,7 @@ Pipeline:
| fir rust | 0.28 | 39.57 | 67.00 | 98.50 |
| fir sse4.1 | 0.28 | 9.63 | 14.13 | 19.84 |
| fir avx2 | 0.28 | 7.73 | 9.67 | 14.66 |
[comment]: <> (bench_compare_rgb end)
### Resize RGBA8 image (U8x4) 4928x3279 => 852x567
@@ -49,12 +51,14 @@ Pipeline:
- Numbers in table is mean duration of image resizing in milliseconds.
- The `image` crate does not support multiplying and dividing by alpha channel.
[comment]: <> (bench_compare_rgba start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|------------|:-------:|:--------:|:----------:|:--------:|
| resize | - | 65.74 | 130.18 | 194.13 |
| fir rust | 0.19 | 35.61 | 52.50 | 75.76 |
| fir sse4.1 | 0.19 | 13.19 | 17.25 | 22.62 |
| fir avx2 | 0.19 | 9.57 | 11.97 | 16.37 |
[comment]: <> (bench_compare_rgba end)
### Resize L8 image (U8) 4928x3279 => 852x567
@@ -66,6 +70,7 @@ Pipeline:
has converted into grayscale image with one byte per pixel.
- Numbers in table is mean duration of image resizing in milliseconds.
[comment]: <> (bench_compare_l start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|------------|:-------:|:--------:|:----------:|:--------:|
| image | 16.97 | 48.47 | 77.55 | 107.48 |
@@ -73,6 +78,7 @@ Pipeline:
| fir rust | 0.15 | 13.44 | 14.73 | 22.58 |
| fir sse4.1 | 0.15 | 4.99 | 5.28 | 8.05 |
| fir avx2 | 0.15 | 7.09 | 5.18 | 8.60 |
[comment]: <> (bench_compare_l end)
### Resize LA8 image (U8x2) 4928x3279 => 852x567
@@ -87,11 +93,13 @@ Pipeline:
- The `image` crate does not support multiplying and dividing by alpha channel.
- The `resize` crate does not support this pixel format.
[comment]: <> (bench_compare_la start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|------------|:-------:|:--------:|:----------:|:--------:|
| fir rust | 0.17 | 25.43 | 28.72 | 39.33 |
| fir sse4.1 | 0.17 | 12.66 | 14.10 | 17.66 |
| fir avx2 | 0.17 | 8.76 | 9.70 | 12.28 |
[comment]: <> (bench_compare_la end)
### Resize RGB16 image (U16x3) 4928x3279 => 852x567
@@ -103,6 +111,7 @@ Pipeline:
has converted into RGB16 image.
- Numbers in table is mean duration of image resizing in milliseconds.
[comment]: <> (bench_compare_rgb16 start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|------------|:-------:|:--------:|:----------:|:--------:|
| image | 20.82 | 75.42 | 127.53 | 179.75 |
@@ -110,6 +119,7 @@ Pipeline:
| fir rust | 0.33 | 43.32 | 78.99 | 113.80 |
| fir sse4.1 | 0.33 | 24.42 | 39.46 | 55.70 |
| fir avx2 | 0.33 | 20.17 | 30.73 | 36.83 |
[comment]: <> (bench_compare_rgb16 end)
### Resize RGBA16 image (U16x4) 4928x3279 => 852x567
@@ -122,12 +132,14 @@ Pipeline:
- Numbers in table is mean duration of image resizing in milliseconds.
- The `image` crate does not support multiplying and dividing by alpha channel.
[comment]: <> (bench_compare_rgba16 start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|------------|:-------:|:--------:|:----------:|:--------:|
| resize | - | 63.82 | 126.13 | 187.90 |
| fir rust | 0.30 | 79.65 | 117.15 | 157.65 |
| fir sse4.1 | 0.30 | 43.40 | 64.83 | 86.98 |
| fir avx2 | 0.30 | 25.63 | 36.85 | 48.28 |
[comment]: <> (bench_compare_rgba16 end)
### Resize L16 image (U16) 4928x3279 => 852x567
@@ -139,6 +151,7 @@ Pipeline:
has converted into grayscale image with two bytes per pixel.
- Numbers in table is mean duration of image resizing in milliseconds.
[comment]: <> (bench_compare_l16 start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|------------|:-------:|:--------:|:----------:|:--------:|
| image | 17.41 | 49.38 | 78.97 | 109.71 |
@@ -146,6 +159,7 @@ Pipeline:
| fir rust | 0.16 | 19.20 | 27.82 | 38.54 |
| fir sse4.1 | 0.16 | 8.07 | 13.39 | 19.30 |
| fir avx2 | 0.16 | 6.61 | 8.60 | 13.67 |
[comment]: <> (bench_compare_l16 end)
### Resize LA16 (luma with alpha channel) image (U16x2) 4928x3279 => 852x567
@@ -160,8 +174,10 @@ Pipeline:
- The `image` crate does not support multiplying and dividing by alpha channel.
- The `resize` crate does not support this pixel format.
[comment]: <> (bench_compare_la16 start)
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|------------|:-------:|:--------:|:----------:|:--------:|
| fir rust | 0.19 | 34.47 | 52.74 | 71.32 |
| fir sse4.1 | 0.19 | 22.10 | 34.18 | 46.62 |
| fir avx2 | 0.19 | 15.17 | 21.85 | 29.09 |
[comment]: <> (bench_compare_la16 end)
+54
View File
@@ -0,0 +1,54 @@
# Preparation
Install additional toolchains.
- Arm64:
```shell
rustup target add aarch64-unknown-linux-gnu
```
- Wasm32:
```shell
rustup target add wasm32-wasi
cargo install cargo-wasi
```
Install [Wasmtime](https://wasmtime.dev/).
# Tests
Run tests without saving result images as files in `./data` directory:
```shell
DONT_SAVE_RESULT=1 cargo test
```
# Wasm32
Specify build target in `.cargo/config.toml` file.
```toml
[build]
target = "wasm32-wasi"
```
Template of command to run `cargo` commands with using `Wasmtime`:
```
CARGO_TARGET_WASM32_WASI_RUNNER="wasmtime --dir=." cargo wasi <any cargo command>
```
Run tests:
```shell
CARGO_TARGET_WASM32_WASI_RUNNER="wasmtime --dir=." cargo wasi test
```
Run tests without saving result images as files in `./data` directory:
```shell
CARGO_TARGET_WASM32_WASI_RUNNER="wasmtime --dir=. --env DONT_SAVE_RESULT=1" cargo wasi test
```
Run a specific benchmark in `quick` mode:
```shell
CARGO_TARGET_WASM32_WASI_RUNNER="wasmtime --dir=." cargo wasi bench --bench bench_resize -- --quick
```
Run benchmarks to compare with other image resize crates and write results into
report files, such as `./benchmarks-x86_64.md`:
```shell
CARGO_TARGET_WASM32_WASI_RUNNER="wasmtime --dir=. --env WRITE_COMPARE_RESULT=1" cargo wasi bench -- Compare
```