diff --git a/bench/Cargo.toml b/bench/Cargo.toml index d5ae950..d7973af 100644 --- a/bench/Cargo.toml +++ b/bench/Cargo.toml @@ -8,11 +8,12 @@ publish = false [dependencies] # internal +libqoi = { path = "../libqoi" } qoi-fast = { path = ".." } # external anyhow = "1.0" -libc = "0.2" bytemuck = "1.7" +c_vec = "2.0" png = "0.17" structopt = "0.3" walkdir = "2.3" diff --git a/bench/build.rs b/bench/build.rs deleted file mode 100644 index f69cb6a..0000000 --- a/bench/build.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::env; -use std::fs; -use std::path::PathBuf; - -fn main() { - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - let out_src = out_dir.join("qoi.c"); - fs::write(&out_src, "#include \"qoi.h\"\n").unwrap(); - - cc::Build::new() - .file(&out_src) - .include("../ext/qoi") - .define("QOI_NO_STDIO", None) - .define("QOI_IMPLEMENTATION", None) - .flag("-Wno-unsequenced") - .opt_level(3) - .compile("qoi"); -} diff --git a/bench/src/main.rs b/bench/src/main.rs index 1f81f7b..2e257da 100644 --- a/bench/src/main.rs +++ b/bench/src/main.rs @@ -1,32 +1,14 @@ use std::cmp::Ordering; use std::fs::{self, File}; use std::path::{Path, PathBuf}; -use std::ptr; use std::time::{Duration, Instant}; use anyhow::{bail, ensure, Context, Result}; -use libc::{c_int, c_void}; use bytemuck::cast_slice; +use c_vec::CVec; use structopt::StructOpt; use walkdir::{DirEntry, WalkDir}; -#[derive(Debug, Copy, Clone, Default)] -#[repr(C)] -#[allow(non_camel_case_types)] -struct qoi_desc { - width: u32, - height: u32, - channels: u8, - colorspace: u8, -} - -extern "C" { - fn qoi_encode(data: *const c_void, desc: *const qoi_desc, out_len: *mut c_int) -> *mut c_void; - fn qoi_decode( - data: *const c_void, size: c_int, desc: *mut qoi_desc, channels: c_int, - ) -> *mut c_void; -} - fn black_box(dummy: T) -> T { unsafe { let ret = core::ptr::read_volatile(&dummy); @@ -136,26 +118,18 @@ impl Image { } trait Codec { + type Output: AsRef<[u8]>; + fn name() -> &'static str; - - fn encode(img: &Image) -> Result>; - - fn encode_bench(img: &Image) -> Result<()> { - let _ = black_box(Self::encode(img)?); - Ok(()) - } - - fn decode(data: &[u8], img: &Image) -> Result>; - - fn decode_bench(data: &[u8], img: &Image) -> Result<()> { - let _ = black_box(Self::decode(data, img)?); - Ok(()) - } + fn encode(img: &Image) -> Result; + fn decode(data: &[u8], img: &Image) -> Result; } struct CodecQoiFast; impl Codec for CodecQoiFast { + type Output = Vec; + fn name() -> &'static str { "qoi-fast" } @@ -171,70 +145,19 @@ impl Codec for CodecQoiFast { struct CodecQoiC; -impl CodecQoiC { - unsafe fn qoi_encode(img: &Image) -> Result<(*mut u8, usize)> { - let desc = qoi_desc { - width: img.width, - height: img.height, - channels: img.channels, - colorspace: 0, - }; - let mut out_len: c_int = 0; - let ptr = - qoi_encode(img.data.as_ptr() as *const _, &desc as *const _, &mut out_len as *mut _); - ensure!(!ptr.is_null(), "error encoding with qoi-c"); - Ok((ptr as _, out_len as _)) - } - - unsafe fn qoi_decode(data: &[u8], img: &Image) -> Result<(*mut u8, qoi_desc)> { - let mut desc = qoi_desc::default(); - let ptr = - qoi_decode(data.as_ptr() as _, data.len() as _, &mut desc as *mut _, img.channels as _); - ensure!(!ptr.is_null(), "error decoding with qoi-c"); - Ok((ptr as _, desc)) - } -} - impl Codec for CodecQoiC { + type Output = CVec; + fn name() -> &'static str { "qoi-c" } - fn encode(img: &Image) -> Result> { - unsafe { - let (ptr, len) = Self::qoi_encode(img)?; - let mut vec = vec![0; len]; - ptr::copy_nonoverlapping(ptr, vec.as_mut_ptr(), len); - libc::free(ptr as _); - Ok(vec) - } + fn encode(img: &Image) -> Result> { + libqoi::qoi_encode(&img.data, img.width, img.height, img.channels) } - fn encode_bench(img: &Image) -> Result<()> { - unsafe { - let (ptr, _) = Self::qoi_encode(img)?; - libc::free(ptr as _); - Ok(()) - } - } - - fn decode(data: &[u8], img: &Image) -> Result> { - unsafe { - let (ptr, desc) = Self::qoi_decode(data, img)?; - let len = desc.width as usize * desc.height as usize * desc.channels as usize; - let mut vec = vec![0; len]; - ptr::copy_nonoverlapping(ptr, vec.as_mut_ptr(), len); - libc::free(ptr as _); - Ok(vec) - } - } - - fn decode_bench(data: &[u8], img: &Image) -> Result<()> { - unsafe { - let (ptr, _) = Self::qoi_decode(data, img)?; - libc::free(ptr as _); - Ok(()) - } + fn decode(data: &[u8], img: &Image) -> Result> { + Ok(libqoi::qoi_decode(data, img.channels)?.1) } } @@ -284,9 +207,9 @@ impl ImageBench { pub fn run(&mut self, img: &Image, sec_allowed: f64) -> Result<()> { let (encoded, t_encode) = timeit(|| C::encode(img)); let encoded = encoded?; - let (decoded, t_decode) = timeit(|| C::decode(&encoded, img)); + let (decoded, t_decode) = timeit(|| C::decode(encoded.as_ref(), img)); let decoded = decoded?; - let roundtrip = decoded.as_slice() == img.data.as_slice(); + let roundtrip = decoded.as_ref() == img.data.as_slice(); if C::name() == "qoi-fast" { assert!(roundtrip, "{}: decoded data doesn't roundtrip", C::name()); } else { @@ -296,14 +219,14 @@ impl ImageBench { let n_encode = (sec_allowed / 2. / t_encode.as_secs_f64()).max(2.).ceil() as usize; let mut encode_tm = Vec::with_capacity(n_encode); for _ in 0..n_encode { - encode_tm.push(timeit(|| C::encode_bench(img)).1); + encode_tm.push(timeit(|| C::encode(img)).1); } let encode_sec = encode_tm.iter().map(Duration::as_secs_f64).collect(); let n_decode = (sec_allowed / 2. / t_decode.as_secs_f64()).max(2.).ceil() as usize; let mut decode_tm = Vec::with_capacity(n_decode); for _ in 0..n_decode { - decode_tm.push(timeit(|| C::decode_bench(&encoded, img)).1); + decode_tm.push(timeit(|| C::decode(encoded.as_ref(), img)).1); } let decode_sec = decode_tm.iter().map(Duration::as_secs_f64).collect(); diff --git a/bench/src/qoi.c b/bench/src/qoi.c deleted file mode 100644 index 2027fd9..0000000 --- a/bench/src/qoi.c +++ /dev/null @@ -1 +0,0 @@ -#include "qoi.h"