qoi-bench: use C bindings from libqoi
This commit is contained in:
parent
497d188f9a
commit
112fd637e6
4 changed files with 19 additions and 114 deletions
|
@ -8,11 +8,12 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# internal
|
# internal
|
||||||
|
libqoi = { path = "../libqoi" }
|
||||||
qoi-fast = { path = ".." }
|
qoi-fast = { path = ".." }
|
||||||
# external
|
# external
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
libc = "0.2"
|
|
||||||
bytemuck = "1.7"
|
bytemuck = "1.7"
|
||||||
|
c_vec = "2.0"
|
||||||
png = "0.17"
|
png = "0.17"
|
||||||
structopt = "0.3"
|
structopt = "0.3"
|
||||||
walkdir = "2.3"
|
walkdir = "2.3"
|
||||||
|
|
|
@ -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");
|
|
||||||
}
|
|
|
@ -1,32 +1,14 @@
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::ptr;
|
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use anyhow::{bail, ensure, Context, Result};
|
use anyhow::{bail, ensure, Context, Result};
|
||||||
use libc::{c_int, c_void};
|
|
||||||
use bytemuck::cast_slice;
|
use bytemuck::cast_slice;
|
||||||
|
use c_vec::CVec;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use walkdir::{DirEntry, WalkDir};
|
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<T>(dummy: T) -> T {
|
fn black_box<T>(dummy: T) -> T {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ret = core::ptr::read_volatile(&dummy);
|
let ret = core::ptr::read_volatile(&dummy);
|
||||||
|
@ -136,26 +118,18 @@ impl Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Codec {
|
trait Codec {
|
||||||
|
type Output: AsRef<[u8]>;
|
||||||
|
|
||||||
fn name() -> &'static str;
|
fn name() -> &'static str;
|
||||||
|
fn encode(img: &Image) -> Result<Self::Output>;
|
||||||
fn encode(img: &Image) -> Result<Vec<u8>>;
|
fn decode(data: &[u8], img: &Image) -> Result<Self::Output>;
|
||||||
|
|
||||||
fn encode_bench(img: &Image) -> Result<()> {
|
|
||||||
let _ = black_box(Self::encode(img)?);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decode(data: &[u8], img: &Image) -> Result<Vec<u8>>;
|
|
||||||
|
|
||||||
fn decode_bench(data: &[u8], img: &Image) -> Result<()> {
|
|
||||||
let _ = black_box(Self::decode(data, img)?);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CodecQoiFast;
|
struct CodecQoiFast;
|
||||||
|
|
||||||
impl Codec for CodecQoiFast {
|
impl Codec for CodecQoiFast {
|
||||||
|
type Output = Vec<u8>;
|
||||||
|
|
||||||
fn name() -> &'static str {
|
fn name() -> &'static str {
|
||||||
"qoi-fast"
|
"qoi-fast"
|
||||||
}
|
}
|
||||||
|
@ -171,70 +145,19 @@ impl Codec for CodecQoiFast {
|
||||||
|
|
||||||
struct CodecQoiC;
|
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 {
|
impl Codec for CodecQoiC {
|
||||||
|
type Output = CVec<u8>;
|
||||||
|
|
||||||
fn name() -> &'static str {
|
fn name() -> &'static str {
|
||||||
"qoi-c"
|
"qoi-c"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode(img: &Image) -> Result<Vec<u8>> {
|
fn encode(img: &Image) -> Result<CVec<u8>> {
|
||||||
unsafe {
|
libqoi::qoi_encode(&img.data, img.width, img.height, img.channels)
|
||||||
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_bench(img: &Image) -> Result<()> {
|
fn decode(data: &[u8], img: &Image) -> Result<CVec<u8>> {
|
||||||
unsafe {
|
Ok(libqoi::qoi_decode(data, img.channels)?.1)
|
||||||
let (ptr, _) = Self::qoi_encode(img)?;
|
|
||||||
libc::free(ptr as _);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decode(data: &[u8], img: &Image) -> Result<Vec<u8>> {
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,9 +207,9 @@ impl ImageBench {
|
||||||
pub fn run<C: Codec>(&mut self, img: &Image, sec_allowed: f64) -> Result<()> {
|
pub fn run<C: Codec>(&mut self, img: &Image, sec_allowed: f64) -> Result<()> {
|
||||||
let (encoded, t_encode) = timeit(|| C::encode(img));
|
let (encoded, t_encode) = timeit(|| C::encode(img));
|
||||||
let encoded = encoded?;
|
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 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" {
|
if C::name() == "qoi-fast" {
|
||||||
assert!(roundtrip, "{}: decoded data doesn't roundtrip", C::name());
|
assert!(roundtrip, "{}: decoded data doesn't roundtrip", C::name());
|
||||||
} else {
|
} else {
|
||||||
|
@ -296,14 +219,14 @@ impl ImageBench {
|
||||||
let n_encode = (sec_allowed / 2. / t_encode.as_secs_f64()).max(2.).ceil() as usize;
|
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);
|
let mut encode_tm = Vec::with_capacity(n_encode);
|
||||||
for _ in 0..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 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 n_decode = (sec_allowed / 2. / t_decode.as_secs_f64()).max(2.).ceil() as usize;
|
||||||
let mut decode_tm = Vec::with_capacity(n_decode);
|
let mut decode_tm = Vec::with_capacity(n_decode);
|
||||||
for _ in 0..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();
|
let decode_sec = decode_tm.iter().map(Duration::as_secs_f64).collect();
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
#include "qoi.h"
|
|
Loading…
Reference in a new issue