feat(compress): move MT sizing policy to Rust
Move target job-log, overlap-log, and overlap-size policy into the Rust multithreaded compression module, retaining the C context projection and logging wrapper. Add policy tests for LDM, strategy defaults, bounds, and overlap sizing. Test Plan: cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression; cargo clippy --manifest-path rust/Cargo.toml; cargo clippy --manifest-path rust/Cargo.toml --benches; cargo clippy --manifest-path rust/Cargo.toml --tests; make -B -C lib -j2 lib; make -B -C programs -j2 zstd zstd-small zstd-frugal; make -B -C tests -j2 test-cli-tests; make -B -C tests -j2 test-zstream
This commit is contained in:
@@ -115,6 +115,12 @@ void ZSTDMT_rust_buffer_pool_release(ZSTDMT_RustBufferPool* pool,
|
||||
ZSTDMT_RustBuffer ZSTDMT_rust_buffer_pool_resize(ZSTDMT_RustBufferPool* pool,
|
||||
ZSTDMT_RustBuffer buffer);
|
||||
|
||||
unsigned ZSTDMT_rust_computeTargetJobLog(unsigned windowLog, unsigned chainLog,
|
||||
int strategy, int enableLdm);
|
||||
int ZSTDMT_rust_overlapLog(int overlapLog, int strategy);
|
||||
size_t ZSTDMT_rust_computeOverlapSize(unsigned windowLog, unsigned chainLog,
|
||||
int strategy, int overlapLog, int enableLdm);
|
||||
|
||||
typedef struct ZSTDMT_bufferPool_s {
|
||||
ZSTDMT_RustBufferPool* rustPool;
|
||||
ZSTD_customMem cMem;
|
||||
@@ -1103,63 +1109,24 @@ size_t ZSTDMT_toFlushNow(ZSTDMT_CCtx* mtctx)
|
||||
|
||||
static unsigned ZSTDMT_computeTargetJobLog(const ZSTD_CCtx_params* params)
|
||||
{
|
||||
unsigned jobLog;
|
||||
if (params->ldmParams.enableLdm == ZSTD_ps_enable) {
|
||||
/* In Long Range Mode, the windowLog is typically oversized.
|
||||
* In which case, it's preferable to determine the jobSize
|
||||
* based on cycleLog instead. */
|
||||
jobLog = MAX(21, ZSTD_cycleLog(params->cParams.chainLog, params->cParams.strategy) + 3);
|
||||
} else {
|
||||
jobLog = MAX(20, params->cParams.windowLog + 2);
|
||||
}
|
||||
return MIN(jobLog, (unsigned)ZSTDMT_JOBLOG_MAX);
|
||||
}
|
||||
|
||||
static int ZSTDMT_overlapLog_default(ZSTD_strategy strat)
|
||||
{
|
||||
switch(strat)
|
||||
{
|
||||
case ZSTD_btultra2:
|
||||
return 9;
|
||||
case ZSTD_btultra:
|
||||
case ZSTD_btopt:
|
||||
return 8;
|
||||
case ZSTD_btlazy2:
|
||||
case ZSTD_lazy2:
|
||||
return 7;
|
||||
case ZSTD_lazy:
|
||||
case ZSTD_greedy:
|
||||
case ZSTD_dfast:
|
||||
case ZSTD_fast:
|
||||
default:;
|
||||
}
|
||||
return 6;
|
||||
}
|
||||
|
||||
static int ZSTDMT_overlapLog(int ovlog, ZSTD_strategy strat)
|
||||
{
|
||||
assert(0 <= ovlog && ovlog <= 9);
|
||||
if (ovlog == 0) return ZSTDMT_overlapLog_default(strat);
|
||||
return ovlog;
|
||||
return ZSTDMT_rust_computeTargetJobLog(params->cParams.windowLog,
|
||||
params->cParams.chainLog,
|
||||
(int)params->cParams.strategy,
|
||||
(int)params->ldmParams.enableLdm);
|
||||
}
|
||||
|
||||
static size_t ZSTDMT_computeOverlapSize(const ZSTD_CCtx_params* params)
|
||||
{
|
||||
int const overlapRLog = 9 - ZSTDMT_overlapLog(params->overlapLog, params->cParams.strategy);
|
||||
int ovLog = (overlapRLog >= 8) ? 0 : (params->cParams.windowLog - overlapRLog);
|
||||
assert(0 <= overlapRLog && overlapRLog <= 8);
|
||||
if (params->ldmParams.enableLdm == ZSTD_ps_enable) {
|
||||
/* In Long Range Mode, the windowLog is typically oversized.
|
||||
* In which case, it's preferable to determine the jobSize
|
||||
* based on chainLog instead.
|
||||
* Then, ovLog becomes a fraction of the jobSize, rather than windowSize */
|
||||
ovLog = MIN(params->cParams.windowLog, ZSTDMT_computeTargetJobLog(params) - 2)
|
||||
- overlapRLog;
|
||||
}
|
||||
assert(0 <= ovLog && ovLog <= ZSTD_WINDOWLOG_MAX);
|
||||
size_t overlapSize;
|
||||
assert(0 <= params->overlapLog && params->overlapLog <= 9);
|
||||
overlapSize = ZSTDMT_rust_computeOverlapSize(params->cParams.windowLog,
|
||||
params->cParams.chainLog,
|
||||
(int)params->cParams.strategy,
|
||||
params->overlapLog,
|
||||
(int)params->ldmParams.enableLdm);
|
||||
DEBUGLOG(4, "overlapLog : %i", params->overlapLog);
|
||||
DEBUGLOG(4, "overlap size : %i", 1 << ovLog);
|
||||
return (ovLog==0) ? 0 : (size_t)1 << ovLog;
|
||||
DEBUGLOG(4, "overlap size : %i", (int)(overlapSize == 0 ? 1 : overlapSize));
|
||||
return overlapSize;
|
||||
}
|
||||
|
||||
/* ====================================== */
|
||||
|
||||
+194
-5
@@ -7,16 +7,138 @@
|
||||
//!
|
||||
//! The serial LDM state, job descriptor fields, worker callback, and streaming
|
||||
//! state still use private C layouts. `zstdmt_compress.c` therefore keeps
|
||||
//! those operations and projects only allocation/lifecycle pieces into this
|
||||
//! module. The entry points below are narrow C ABIs: buffers, `ZSTD_CCtx *`
|
||||
//! values, and job descriptors remain opaque to Rust, while allocation, reuse,
|
||||
//! expansion, and synchronization of Rust-owned state are Rust-owned.
|
||||
//! those operations and projects only allocation/lifecycle pieces and pure
|
||||
//! sizing policy into this module. The entry points below are narrow C ABIs:
|
||||
//! buffers, `ZSTD_CCtx *` values, and job descriptors remain opaque to Rust,
|
||||
//! while allocation, reuse, expansion, synchronization, and sizing policy are
|
||||
//! Rust-owned.
|
||||
|
||||
use std::mem::{self, MaybeUninit};
|
||||
use std::os::raw::{c_uint, c_void};
|
||||
use std::os::raw::{c_int, c_uint, c_void};
|
||||
use std::ptr;
|
||||
use std::sync::Mutex;
|
||||
|
||||
const ZSTDMT_JOBLOG_MAX: c_uint = if mem::size_of::<usize>() == 4 { 29 } else { 30 };
|
||||
const ZSTD_WINDOWLOG_MAX: c_uint = if mem::size_of::<usize>() == 4 { 30 } else { 31 };
|
||||
|
||||
const ZSTD_FAST: c_int = 1;
|
||||
const ZSTD_DFAST: c_int = 2;
|
||||
const ZSTD_GREEDY: c_int = 3;
|
||||
const ZSTD_LAZY: c_int = 4;
|
||||
const ZSTD_LAZY2: c_int = 5;
|
||||
const ZSTD_BTLAZY2: c_int = 6;
|
||||
const ZSTD_BTOPT: c_int = 7;
|
||||
const ZSTD_BTULTRA: c_int = 8;
|
||||
const ZSTD_BTULTRA2: c_int = 9;
|
||||
const ZSTD_PS_ENABLE: c_int = 1;
|
||||
#[cfg(test)]
|
||||
const ZSTD_PS_DISABLE: c_int = 2;
|
||||
|
||||
#[inline]
|
||||
fn cycle_log(chain_log: c_uint, strategy: c_int) -> c_uint {
|
||||
chain_log.wrapping_sub((strategy >= ZSTD_BTLAZY2) as c_uint)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn compute_target_job_log(
|
||||
window_log: c_uint,
|
||||
chain_log: c_uint,
|
||||
strategy: c_int,
|
||||
enable_ldm: c_int,
|
||||
) -> c_uint {
|
||||
let job_log = if enable_ldm == ZSTD_PS_ENABLE {
|
||||
/* In Long Range Mode, the windowLog is typically oversized.
|
||||
* In which case, it's preferable to determine the jobSize
|
||||
* based on cycleLog instead. */
|
||||
21.max(cycle_log(chain_log, strategy).wrapping_add(3))
|
||||
} else {
|
||||
20.max(window_log.wrapping_add(2))
|
||||
};
|
||||
job_log.min(ZSTDMT_JOBLOG_MAX)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn overlap_log_default(strategy: c_int) -> c_int {
|
||||
match strategy {
|
||||
ZSTD_BTULTRA2 => 9,
|
||||
ZSTD_BTULTRA | ZSTD_BTOPT => 8,
|
||||
ZSTD_BTLAZY2 | ZSTD_LAZY2 => 7,
|
||||
ZSTD_LAZY | ZSTD_GREEDY | ZSTD_DFAST | ZSTD_FAST => 6,
|
||||
_ => 6,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn overlap_log(overlap_log: c_int, strategy: c_int) -> c_int {
|
||||
debug_assert!((0..=9).contains(&overlap_log));
|
||||
if overlap_log == 0 {
|
||||
overlap_log_default(strategy)
|
||||
} else {
|
||||
overlap_log
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn compute_overlap_size(
|
||||
window_log: c_uint,
|
||||
chain_log: c_uint,
|
||||
strategy: c_int,
|
||||
overlap_log_value: c_int,
|
||||
enable_ldm: c_int,
|
||||
) -> usize {
|
||||
let overlap_r_log = 9 - overlap_log(overlap_log_value, strategy);
|
||||
let mut overlap_log_value = if overlap_r_log >= 8 {
|
||||
0
|
||||
} else {
|
||||
window_log as c_int - overlap_r_log
|
||||
};
|
||||
debug_assert!((0..=8).contains(&overlap_r_log));
|
||||
if enable_ldm == ZSTD_PS_ENABLE {
|
||||
/* In Long Range Mode, the windowLog is typically oversized.
|
||||
* In which case, it's preferable to determine the jobSize
|
||||
* based on chainLog instead.
|
||||
* Then, ovLog becomes a fraction of the jobSize, rather than windowSize */
|
||||
let target_job_log = compute_target_job_log(window_log, chain_log, strategy, enable_ldm);
|
||||
overlap_log_value = window_log.min(target_job_log.wrapping_sub(2)) as c_int - overlap_r_log;
|
||||
}
|
||||
debug_assert!(overlap_log_value >= 0);
|
||||
debug_assert!(overlap_log_value <= ZSTD_WINDOWLOG_MAX as c_int);
|
||||
if overlap_log_value == 0 {
|
||||
0
|
||||
} else {
|
||||
1usize << overlap_log_value as usize
|
||||
}
|
||||
}
|
||||
|
||||
/// C ABI for the pure MT target job-log policy.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ZSTDMT_rust_computeTargetJobLog(
|
||||
windowLog: c_uint,
|
||||
chainLog: c_uint,
|
||||
strategy: c_int,
|
||||
enableLdm: c_int,
|
||||
) -> c_uint {
|
||||
compute_target_job_log(windowLog, chainLog, strategy, enableLdm)
|
||||
}
|
||||
|
||||
/// C ABI for the MT overlap-log default/selection policy.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ZSTDMT_rust_overlapLog(overlapLog: c_int, strategy: c_int) -> c_int {
|
||||
overlap_log(overlapLog, strategy)
|
||||
}
|
||||
|
||||
/// C ABI for the pure MT overlap-size policy.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ZSTDMT_rust_computeOverlapSize(
|
||||
windowLog: c_uint,
|
||||
chainLog: c_uint,
|
||||
strategy: c_int,
|
||||
overlapLog: c_int,
|
||||
enableLdm: c_int,
|
||||
) -> usize {
|
||||
compute_overlap_size(windowLog, chainLog, strategy, overlapLog, enableLdm)
|
||||
}
|
||||
|
||||
type ZstdAllocFunction = unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void;
|
||||
type ZstdFreeFunction = unsafe extern "C" fn(*mut c_void, *mut c_void);
|
||||
|
||||
@@ -680,4 +802,71 @@ mod tests {
|
||||
assert_eq!(rounded_job_count(255), Some(256));
|
||||
assert_eq!(rounded_job_count(256), Some(512));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn target_job_log_preserves_ldm_and_non_ldm_policy() {
|
||||
assert_eq!(
|
||||
compute_target_job_log(18, 25, ZSTD_FAST, ZSTD_PS_DISABLE),
|
||||
20
|
||||
);
|
||||
assert_eq!(
|
||||
compute_target_job_log(30, 25, ZSTD_FAST, ZSTD_PS_DISABLE),
|
||||
ZSTDMT_JOBLOG_MAX
|
||||
);
|
||||
assert_eq!(
|
||||
compute_target_job_log(10, 25, ZSTD_FAST, ZSTD_PS_ENABLE),
|
||||
28
|
||||
);
|
||||
assert_eq!(
|
||||
compute_target_job_log(30, 25, ZSTD_BTULTRA2, ZSTD_PS_ENABLE),
|
||||
27
|
||||
);
|
||||
assert_eq!(
|
||||
compute_target_job_log(10, 30, ZSTD_BTULTRA2, ZSTD_PS_ENABLE),
|
||||
ZSTDMT_JOBLOG_MAX
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlap_log_defaults_follow_strategy_groups() {
|
||||
assert_eq!(overlap_log_default(ZSTD_FAST), 6);
|
||||
assert_eq!(overlap_log_default(ZSTD_DFAST), 6);
|
||||
assert_eq!(overlap_log_default(ZSTD_GREEDY), 6);
|
||||
assert_eq!(overlap_log_default(ZSTD_LAZY), 6);
|
||||
assert_eq!(overlap_log_default(ZSTD_LAZY2), 7);
|
||||
assert_eq!(overlap_log_default(ZSTD_BTLAZY2), 7);
|
||||
assert_eq!(overlap_log_default(ZSTD_BTOPT), 8);
|
||||
assert_eq!(overlap_log_default(ZSTD_BTULTRA), 8);
|
||||
assert_eq!(overlap_log_default(ZSTD_BTULTRA2), 9);
|
||||
assert_eq!(overlap_log_default(0), 6);
|
||||
|
||||
assert_eq!(overlap_log(0, ZSTD_BTULTRA2), 9);
|
||||
assert_eq!(overlap_log(0, ZSTD_FAST), 6);
|
||||
assert_eq!(overlap_log(5, ZSTD_BTULTRA2), 5);
|
||||
assert_eq!(overlap_log(9, ZSTD_FAST), 9);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlap_size_uses_window_or_target_job_log_as_expected() {
|
||||
assert_eq!(
|
||||
compute_overlap_size(20, 25, ZSTD_FAST, 0, ZSTD_PS_DISABLE),
|
||||
1usize << 17
|
||||
);
|
||||
assert_eq!(
|
||||
compute_overlap_size(20, 25, ZSTD_BTULTRA2, 0, ZSTD_PS_DISABLE),
|
||||
1usize << 20
|
||||
);
|
||||
assert_eq!(
|
||||
compute_overlap_size(20, 25, ZSTD_FAST, 1, ZSTD_PS_DISABLE),
|
||||
0
|
||||
);
|
||||
assert_eq!(
|
||||
compute_overlap_size(30, 25, ZSTD_FAST, 0, ZSTD_PS_ENABLE),
|
||||
1usize << 23
|
||||
);
|
||||
assert_eq!(
|
||||
compute_overlap_size(30, 25, ZSTD_BTULTRA2, 0, ZSTD_PS_ENABLE),
|
||||
1usize << 25
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user