Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use an object pool for ZopfliHash instances #28

Merged
merged 12 commits into from
Mar 18, 2024
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ crc32fast = { version = "1.4.0", default-features = false, optional = true }
simd-adler32 = { version = "0.3.7", default-features = false, optional = true }
typed-arena = { version = "2.0.2", default-features = false }
log = "0.4.21"
lockfree-object-pool = "0.1.5"
once_cell = "1.19.0"

[dev-dependencies]
proptest = "1.4.0"
Expand Down
6 changes: 6 additions & 0 deletions src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ use alloc::{
};
use core::ptr::{addr_of, addr_of_mut, NonNull};

use lockfree_object_pool::LinearObjectPool;
use once_cell::sync::Lazy;

use crate::util::{ZOPFLI_MIN_MATCH, ZOPFLI_WINDOW_MASK, ZOPFLI_WINDOW_SIZE};

const HASH_SHIFT: i32 = 5;
Expand Down Expand Up @@ -176,3 +179,6 @@ impl ZopfliHash {
}
}
}

pub static HASH_POOL: Lazy<LinearObjectPool<Box<ZopfliHash>>> =
Lazy::new(|| LinearObjectPool::new(ZopfliHash::new, |boxed| boxed.reset()));
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ use std::io::{Error, Write};
#[cfg(any(doc, not(feature = "std")))]
pub use io::{Error, ErrorKind, Write};

use crate::hash::HASH_POOL;

/// Options for the Zopfli compression algorithm.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(all(test, feature = "std"), derive(proptest_derive::Arbitrary))]
Expand Down Expand Up @@ -169,6 +171,12 @@ pub fn compress<R: std::io::Read, W: Write>(
}
}

/// Populates object pools for expensive objects that Zopfli uses. Call this on a background thread
/// when you know ahead of time that compression will be needed.
pub fn prewarm_object_pools() {
HASH_POOL.pull();
}

#[cfg(all(test, feature = "std"))]
mod test {
use std::io;
Expand Down
8 changes: 5 additions & 3 deletions src/lz77.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use core::cmp;

use crate::{
cache::Cache,
hash::{Which, ZopfliHash},
hash::{Which, ZopfliHash, HASH_POOL},
symbols::{get_dist_symbol, get_length_symbol},
util::{
boxed_array, ZOPFLI_MAX_CHAIN_HITS, ZOPFLI_MAX_MATCH, ZOPFLI_MIN_MATCH, ZOPFLI_NUM_D,
Expand Down Expand Up @@ -139,7 +139,8 @@ impl Lz77Store {
return;
}
let windowstart = instart.saturating_sub(ZOPFLI_WINDOW_SIZE);
let mut h = ZopfliHash::new();
let hash_pool = &*HASH_POOL;
let mut h = hash_pool.pull();

let arr = &in_data[..inend];
h.warmup(arr, windowstart, inend);
Expand Down Expand Up @@ -239,7 +240,8 @@ impl Lz77Store {
return;
}

let mut h = ZopfliHash::new();
let hash_pool = &*HASH_POOL;
let mut h = hash_pool.pull();

let arr = &in_data[..inend];
h.warmup(arr, windowstart, inend);
Expand Down
8 changes: 5 additions & 3 deletions src/squeeze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use log::{debug, trace};
use crate::{
cache::Cache,
deflate::{calculate_block_size, BlockType},
hash::ZopfliHash,
hash::{ZopfliHash, HASH_POOL},
lz77::{find_longest_match, LitLen, Lz77Store},
symbols::{get_dist_extra_bits, get_dist_symbol, get_length_extra_bits, get_length_symbol},
util::{ZOPFLI_MAX_MATCH, ZOPFLI_NUM_D, ZOPFLI_NUM_LL, ZOPFLI_WINDOW_MASK, ZOPFLI_WINDOW_SIZE},
Expand Down Expand Up @@ -421,7 +421,8 @@ pub fn lz77_optimal_fixed<C: Cache>(
inend: usize,
store: &mut Lz77Store,
) {
let mut h = ZopfliHash::new();
let hash_pool = &*HASH_POOL;
let mut h = hash_pool.pull();
let mut costs = Vec::with_capacity(inend - instart);
lz77_optimal_run(
lmc,
Expand Down Expand Up @@ -455,7 +456,8 @@ pub fn lz77_optimal<C: Cache>(
let mut stats = SymbolStats::default();
stats.get_statistics(&currentstore);

let mut h = ZopfliHash::new();
let hash_pool = &*HASH_POOL;
let mut h = hash_pool.pull();
let mut costs = Vec::with_capacity(inend - instart + 1);

let mut beststats = SymbolStats::default();
Expand Down