2025-12-24 21:42:05 +08:00
|
|
|
use std::time::Instant;
|
|
|
|
|
|
|
|
|
|
use anyhow::Result;
|
|
|
|
|
use image::{ImageReader, Rgba, RgbaImage};
|
|
|
|
|
use rayon::prelude::*;
|
|
|
|
|
|
|
|
|
|
/// 测试像素组合性能对比
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_pixel_combine_performance() -> Result<()> {
|
|
|
|
|
// cargo test test_pixel_combine_performance -r -- --nocapture
|
|
|
|
|
let img_path = "./assets/img/gougou.jpg";
|
|
|
|
|
let img = ImageReader::open(img_path)?.decode()?;
|
|
|
|
|
// 缩小图片以加快测试
|
|
|
|
|
let img = img.resize(1024, 1024, image::imageops::FilterType::Nearest);
|
|
|
|
|
let width = img.width();
|
|
|
|
|
let height = img.height();
|
|
|
|
|
let rgb_img = img.to_rgb8();
|
|
|
|
|
|
|
|
|
|
// 模拟 alpha 通道
|
2025-12-26 00:23:14 +08:00
|
|
|
let alpha_gray =
|
|
|
|
|
image::GrayImage::from_fn(width, height, |x, y| image::Luma([((x + y) % 256) as u8]));
|
2025-12-24 21:42:05 +08:00
|
|
|
|
2025-12-25 10:44:32 +08:00
|
|
|
let iterations = 10;
|
2025-12-24 21:42:05 +08:00
|
|
|
|
|
|
|
|
println!("=== 像素组合性能测试 ===");
|
|
|
|
|
println!("图片尺寸: {}x{}", width, height);
|
|
|
|
|
println!();
|
|
|
|
|
|
|
|
|
|
// 旧方法:逐像素操作
|
|
|
|
|
let start = Instant::now();
|
|
|
|
|
for _ in 0..iterations {
|
|
|
|
|
let mut rgba_img = RgbaImage::new(width, height);
|
|
|
|
|
for (x, y, pixel) in rgb_img.enumerate_pixels() {
|
|
|
|
|
let alpha_value = alpha_gray.get_pixel(x, y).0[0];
|
|
|
|
|
let rgba_pixel = Rgba([pixel.0[0], pixel.0[1], pixel.0[2], alpha_value]);
|
|
|
|
|
rgba_img.put_pixel(x, y, rgba_pixel);
|
|
|
|
|
}
|
|
|
|
|
std::hint::black_box(&rgba_img);
|
|
|
|
|
}
|
|
|
|
|
let old_duration = start.elapsed();
|
|
|
|
|
println!(
|
|
|
|
|
"旧方法(逐像素): {:?}, 平均: {:?}",
|
|
|
|
|
old_duration,
|
|
|
|
|
old_duration / iterations
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 新方法:串行索引赋值
|
|
|
|
|
let start = Instant::now();
|
|
|
|
|
for _ in 0..iterations {
|
|
|
|
|
let rgb_raw = rgb_img.as_raw();
|
|
|
|
|
let alpha_raw = alpha_gray.as_raw();
|
|
|
|
|
let pixel_count = (width * height) as usize;
|
|
|
|
|
let mut rgba_raw = vec![0u8; pixel_count * 4];
|
|
|
|
|
|
|
|
|
|
for i in 0..pixel_count {
|
|
|
|
|
let dst = i * 4;
|
|
|
|
|
let src = i * 3;
|
|
|
|
|
rgba_raw[dst] = rgb_raw[src];
|
|
|
|
|
rgba_raw[dst + 1] = rgb_raw[src + 1];
|
|
|
|
|
rgba_raw[dst + 2] = rgb_raw[src + 2];
|
|
|
|
|
rgba_raw[dst + 3] = alpha_raw[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let rgba_img = RgbaImage::from_raw(width, height, rgba_raw).unwrap();
|
|
|
|
|
std::hint::black_box(&rgba_img);
|
|
|
|
|
}
|
|
|
|
|
let serial_duration = start.elapsed();
|
|
|
|
|
println!(
|
|
|
|
|
"新方法(串行索引): {:?}, 平均: {:?}",
|
|
|
|
|
serial_duration,
|
|
|
|
|
serial_duration / iterations
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 新方法:并行分块写入
|
|
|
|
|
let start = Instant::now();
|
|
|
|
|
for _ in 0..iterations {
|
|
|
|
|
let rgb_raw = rgb_img.as_raw();
|
|
|
|
|
let alpha_raw = alpha_gray.as_raw();
|
|
|
|
|
let pixel_count = (width * height) as usize;
|
|
|
|
|
let mut rgba_raw = vec![0u8; pixel_count * 4];
|
|
|
|
|
|
|
|
|
|
rgba_raw
|
|
|
|
|
.par_chunks_mut(4)
|
|
|
|
|
.enumerate()
|
|
|
|
|
.for_each(|(i, chunk)| {
|
|
|
|
|
let src = i * 3;
|
|
|
|
|
chunk[0] = rgb_raw[src];
|
|
|
|
|
chunk[1] = rgb_raw[src + 1];
|
|
|
|
|
chunk[2] = rgb_raw[src + 2];
|
|
|
|
|
chunk[3] = alpha_raw[i];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let rgba_img = RgbaImage::from_raw(width, height, rgba_raw).unwrap();
|
|
|
|
|
std::hint::black_box(&rgba_img);
|
|
|
|
|
}
|
|
|
|
|
let parallel_duration = start.elapsed();
|
|
|
|
|
println!(
|
|
|
|
|
"新方法(并行): {:?}, 平均: {:?}",
|
|
|
|
|
parallel_duration,
|
|
|
|
|
parallel_duration / iterations
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let speedup_serial = old_duration.as_secs_f64() / serial_duration.as_secs_f64();
|
|
|
|
|
let speedup_parallel = old_duration.as_secs_f64() / parallel_duration.as_secs_f64();
|
|
|
|
|
println!();
|
|
|
|
|
println!("串行索引 vs 逐像素: {:.2}x", speedup_serial);
|
|
|
|
|
println!("并行 vs 逐像素: {:.2}x", speedup_parallel);
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 测试图片 resize 性能对比(串行 vs 并行)
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_image_resize_parallel_vs_serial() -> Result<()> {
|
|
|
|
|
// cargo test test_image_resize_parallel_vs_serial -r -- --nocapture
|
|
|
|
|
let img_path = "./assets/img/gougou.jpg";
|
|
|
|
|
let img = ImageReader::open(img_path)?.decode()?;
|
|
|
|
|
// 缩小原图以加快测试
|
|
|
|
|
let img = img.resize(2048, 2048, image::imageops::FilterType::Nearest);
|
|
|
|
|
|
|
|
|
|
let num_images = 4;
|
|
|
|
|
let imgs: Vec<_> = (0..num_images).map(|_| img.clone()).collect();
|
|
|
|
|
let target_h = 1024u32;
|
|
|
|
|
let target_w = 1024u32;
|
|
|
|
|
|
|
|
|
|
let iterations = 10;
|
|
|
|
|
|
|
|
|
|
println!("=== 图片 Resize 性能测试 ===");
|
|
|
|
|
println!("图片数量: {}", num_images);
|
|
|
|
|
println!("原始尺寸: {}x{}", img.width(), img.height());
|
|
|
|
|
println!("目标尺寸: {}x{}", target_w, target_h);
|
|
|
|
|
println!();
|
|
|
|
|
|
|
|
|
|
// 串行 resize
|
|
|
|
|
let start = Instant::now();
|
|
|
|
|
for _ in 0..iterations {
|
|
|
|
|
let mut results = Vec::with_capacity(num_images);
|
|
|
|
|
for img in &imgs {
|
|
|
|
|
let resized =
|
|
|
|
|
img.resize_exact(target_w, target_h, image::imageops::FilterType::CatmullRom);
|
|
|
|
|
results.push(resized);
|
|
|
|
|
}
|
|
|
|
|
std::hint::black_box(&results);
|
|
|
|
|
}
|
|
|
|
|
let serial_duration = start.elapsed();
|
|
|
|
|
println!(
|
|
|
|
|
"串行 resize: {:?}, 平均: {:?}",
|
|
|
|
|
serial_duration,
|
|
|
|
|
serial_duration / iterations
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 并行 resize
|
|
|
|
|
let start = Instant::now();
|
|
|
|
|
for _ in 0..iterations {
|
|
|
|
|
let results: Vec<_> = imgs
|
|
|
|
|
.par_iter()
|
|
|
|
|
.map(|img| {
|
|
|
|
|
img.resize_exact(target_w, target_h, image::imageops::FilterType::CatmullRom)
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
std::hint::black_box(&results);
|
|
|
|
|
}
|
|
|
|
|
let parallel_duration = start.elapsed();
|
|
|
|
|
println!(
|
|
|
|
|
"并行 resize: {:?}, 平均: {:?}",
|
|
|
|
|
parallel_duration,
|
|
|
|
|
parallel_duration / iterations
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let speedup = serial_duration.as_secs_f64() / parallel_duration.as_secs_f64();
|
|
|
|
|
println!();
|
|
|
|
|
println!("并行 vs 串行: {:.2}x", speedup);
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 测试后处理阶段并行 vs 串行性能(纯图像操作)
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_postprocess_parallel_vs_serial() -> Result<()> {
|
|
|
|
|
// cargo test test_postprocess_parallel_vs_serial -r -- --nocapture
|
|
|
|
|
let img_path = "./assets/img/gougou.jpg";
|
|
|
|
|
let img = ImageReader::open(img_path)?.decode()?;
|
|
|
|
|
// 缩小图片以加快测试
|
|
|
|
|
let img = img.resize(1024, 1024, image::imageops::FilterType::Nearest);
|
|
|
|
|
let width = img.width();
|
|
|
|
|
let height = img.height();
|
|
|
|
|
|
|
|
|
|
// 模拟多张图片的后处理数据
|
|
|
|
|
let num_images = 4;
|
|
|
|
|
let rgb_imgs: Vec<_> = (0..num_images).map(|_| img.to_rgb8()).collect();
|
|
|
|
|
let alpha_grays: Vec<_> = (0..num_images)
|
|
|
|
|
.map(|_| {
|
|
|
|
|
image::GrayImage::from_fn(width, height, |x, y| image::Luma([((x + y) % 256) as u8]))
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
2025-12-25 10:44:32 +08:00
|
|
|
let iterations = 10;
|
2025-12-24 21:42:05 +08:00
|
|
|
|
|
|
|
|
println!("=== 后处理阶段性能测试(纯图像操作)===");
|
|
|
|
|
println!("图片数量: {}", num_images);
|
|
|
|
|
println!("图片尺寸: {}x{}", width, height);
|
|
|
|
|
println!();
|
|
|
|
|
|
|
|
|
|
// 串行后处理(for-in 循环)
|
|
|
|
|
let start = Instant::now();
|
|
|
|
|
for _ in 0..iterations {
|
|
|
|
|
let mut results = Vec::with_capacity(num_images);
|
|
|
|
|
for i in 0..num_images {
|
|
|
|
|
let rgb_raw = rgb_imgs[i].as_raw();
|
|
|
|
|
let alpha_raw = alpha_grays[i].as_raw();
|
|
|
|
|
let pixel_count = (width * height) as usize;
|
|
|
|
|
let mut rgba_raw = vec![0u8; pixel_count * 4];
|
|
|
|
|
|
|
|
|
|
for j in 0..pixel_count {
|
|
|
|
|
let dst = j * 4;
|
|
|
|
|
let src = j * 3;
|
|
|
|
|
rgba_raw[dst] = rgb_raw[src];
|
|
|
|
|
rgba_raw[dst + 1] = rgb_raw[src + 1];
|
|
|
|
|
rgba_raw[dst + 2] = rgb_raw[src + 2];
|
|
|
|
|
rgba_raw[dst + 3] = alpha_raw[j];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let rgba_img = RgbaImage::from_raw(width, height, rgba_raw).unwrap();
|
|
|
|
|
results.push(rgba_img);
|
|
|
|
|
}
|
|
|
|
|
std::hint::black_box(&results);
|
|
|
|
|
}
|
|
|
|
|
let serial_duration = start.elapsed();
|
|
|
|
|
println!(
|
|
|
|
|
"串行后处理: {:?}, 平均: {:?}",
|
|
|
|
|
serial_duration,
|
|
|
|
|
serial_duration / iterations
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 并行后处理(外层并行 + 内层并行)
|
|
|
|
|
let start = Instant::now();
|
|
|
|
|
for _ in 0..iterations {
|
|
|
|
|
let results: Vec<_> = (0..num_images)
|
|
|
|
|
.into_par_iter()
|
|
|
|
|
.map(|i| {
|
|
|
|
|
let rgb_raw = rgb_imgs[i].as_raw();
|
|
|
|
|
let alpha_raw = alpha_grays[i].as_raw();
|
|
|
|
|
let pixel_count = (width * height) as usize;
|
|
|
|
|
let mut rgba_raw = vec![0u8; pixel_count * 4];
|
|
|
|
|
|
|
|
|
|
rgba_raw
|
|
|
|
|
.par_chunks_mut(4)
|
|
|
|
|
.enumerate()
|
|
|
|
|
.for_each(|(j, chunk)| {
|
|
|
|
|
let src = j * 3;
|
|
|
|
|
chunk[0] = rgb_raw[src];
|
|
|
|
|
chunk[1] = rgb_raw[src + 1];
|
|
|
|
|
chunk[2] = rgb_raw[src + 2];
|
|
|
|
|
chunk[3] = alpha_raw[j];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
RgbaImage::from_raw(width, height, rgba_raw).unwrap()
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
std::hint::black_box(&results);
|
|
|
|
|
}
|
|
|
|
|
let parallel_duration = start.elapsed();
|
|
|
|
|
println!(
|
|
|
|
|
"并行后处理: {:?}, 平均: {:?}",
|
|
|
|
|
parallel_duration,
|
|
|
|
|
parallel_duration / iterations
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let speedup = serial_duration.as_secs_f64() / parallel_duration.as_secs_f64();
|
|
|
|
|
println!();
|
|
|
|
|
println!("后处理性能提升: {:.2}x", speedup);
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 测试完整图像处理流程(resize + 像素合并)串行 vs 并行
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_full_image_pipeline_parallel_vs_serial() -> Result<()> {
|
|
|
|
|
// cargo test test_full_image_pipeline_parallel_vs_serial -r -- --nocapture
|
|
|
|
|
let img_path = "./assets/img/gougou.jpg";
|
|
|
|
|
let img = ImageReader::open(img_path)?.decode()?;
|
|
|
|
|
// 缩小图片以加快测试
|
|
|
|
|
let img = img.resize(2048, 2048, image::imageops::FilterType::Nearest);
|
|
|
|
|
let orig_width = img.width();
|
|
|
|
|
let orig_height = img.height();
|
|
|
|
|
|
|
|
|
|
let num_images = 4;
|
|
|
|
|
let imgs: Vec<_> = (0..num_images).map(|_| img.clone()).collect();
|
|
|
|
|
let target_h = 1024u32;
|
|
|
|
|
let target_w = 1024u32;
|
|
|
|
|
|
|
|
|
|
// 模拟 alpha 蒙版
|
|
|
|
|
let alpha_grays: Vec<_> = (0..num_images)
|
|
|
|
|
.map(|_| {
|
|
|
|
|
image::GrayImage::from_fn(orig_width, orig_height, |x, y| {
|
|
|
|
|
image::Luma([((x + y) % 256) as u8])
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
let iterations = 10;
|
|
|
|
|
|
|
|
|
|
println!("=== 完整图像处理流程性能测试 ===");
|
|
|
|
|
println!("图片数量: {}", num_images);
|
|
|
|
|
println!("原始尺寸: {}x{}", orig_width, orig_height);
|
|
|
|
|
println!("处理尺寸: {}x{}", target_w, target_h);
|
|
|
|
|
println!();
|
|
|
|
|
|
|
|
|
|
// 串行处理流程
|
|
|
|
|
let start = Instant::now();
|
|
|
|
|
for _ in 0..iterations {
|
|
|
|
|
let mut results = Vec::with_capacity(num_images);
|
|
|
|
|
for i in 0..num_images {
|
|
|
|
|
// 预处理:resize 到模型输入尺寸
|
|
|
|
|
let _resized =
|
|
|
|
|
imgs[i].resize_exact(target_w, target_h, image::imageops::FilterType::CatmullRom);
|
|
|
|
|
|
|
|
|
|
// 后处理:合并 RGB 和 alpha
|
|
|
|
|
let rgb_img = imgs[i].to_rgb8();
|
|
|
|
|
let rgb_raw = rgb_img.as_raw();
|
|
|
|
|
let alpha_raw = alpha_grays[i].as_raw();
|
|
|
|
|
let pixel_count = (orig_width * orig_height) as usize;
|
|
|
|
|
let mut rgba_raw = vec![0u8; pixel_count * 4];
|
|
|
|
|
|
|
|
|
|
for j in 0..pixel_count {
|
|
|
|
|
let dst = j * 4;
|
|
|
|
|
let src = j * 3;
|
|
|
|
|
rgba_raw[dst] = rgb_raw[src];
|
|
|
|
|
rgba_raw[dst + 1] = rgb_raw[src + 1];
|
|
|
|
|
rgba_raw[dst + 2] = rgb_raw[src + 2];
|
|
|
|
|
rgba_raw[dst + 3] = alpha_raw[j];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let rgba_img = RgbaImage::from_raw(orig_width, orig_height, rgba_raw).unwrap();
|
|
|
|
|
results.push(rgba_img);
|
|
|
|
|
}
|
|
|
|
|
std::hint::black_box(&results);
|
|
|
|
|
}
|
|
|
|
|
let serial_duration = start.elapsed();
|
|
|
|
|
println!(
|
|
|
|
|
"串行流程: {:?}, 平均: {:?}",
|
|
|
|
|
serial_duration,
|
|
|
|
|
serial_duration / iterations
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 并行处理流程
|
|
|
|
|
let start = Instant::now();
|
|
|
|
|
for _ in 0..iterations {
|
|
|
|
|
// 并行预处理
|
|
|
|
|
let _resized: Vec<_> = imgs
|
|
|
|
|
.par_iter()
|
|
|
|
|
.map(|img| {
|
|
|
|
|
img.resize_exact(target_w, target_h, image::imageops::FilterType::CatmullRom)
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
// 并行后处理
|
|
|
|
|
let results: Vec<_> = (0..num_images)
|
|
|
|
|
.into_par_iter()
|
|
|
|
|
.map(|i| {
|
|
|
|
|
let rgb_img = imgs[i].to_rgb8();
|
|
|
|
|
let rgb_raw = rgb_img.as_raw();
|
|
|
|
|
let alpha_raw = alpha_grays[i].as_raw();
|
|
|
|
|
let pixel_count = (orig_width * orig_height) as usize;
|
|
|
|
|
let mut rgba_raw = vec![0u8; pixel_count * 4];
|
|
|
|
|
|
|
|
|
|
rgba_raw
|
|
|
|
|
.par_chunks_mut(4)
|
|
|
|
|
.enumerate()
|
|
|
|
|
.for_each(|(j, chunk)| {
|
|
|
|
|
let src = j * 3;
|
|
|
|
|
chunk[0] = rgb_raw[src];
|
|
|
|
|
chunk[1] = rgb_raw[src + 1];
|
|
|
|
|
chunk[2] = rgb_raw[src + 2];
|
|
|
|
|
chunk[3] = alpha_raw[j];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
RgbaImage::from_raw(orig_width, orig_height, rgba_raw).unwrap()
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
std::hint::black_box(&results);
|
|
|
|
|
}
|
|
|
|
|
let parallel_duration = start.elapsed();
|
|
|
|
|
println!(
|
|
|
|
|
"并行流程: {:?}, 平均: {:?}",
|
|
|
|
|
parallel_duration,
|
|
|
|
|
parallel_duration / iterations
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let speedup = serial_duration.as_secs_f64() / parallel_duration.as_secs_f64();
|
|
|
|
|
println!();
|
|
|
|
|
println!("完整流程性能提升: {:.2}x", speedup);
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|