feat(mt): move serial sequence turn policy into Rust
Move MT serial turn/skip control and the ordering of LDM generation, checksum updates, and turn advancement into the Rust rewrite. Keep the C serial mutexes, LDM window/hash state, checksum state, and codec callbacks behind a narrow callback bridge so the synchronization and private layouts remain unchanged. Add focused tests for skipped predecessors, empty LDM turns, and LDM-before-checksum ordering. Test Plan: - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo fmt --manifest-path rust/Cargo.toml -- --check - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml serial_turn_ -- --nocapture - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo test --manifest-path rust/Cargo.toml - ulimit -v 41943040; CARGO_BUILD_JOBS=1 cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings - ulimit -v 41943040 make -B -C programs -j1 zstd - ulimit -v 41943040 make -C tests -j1 test-zstream ZSTREAM_TESTTIME=-T1s
This commit is contained in:
@@ -354,6 +354,20 @@ typedef char ZSTDMT_rust_raw_seq_store_layout[
|
||||
|
||||
ZSTDMT_RustRawSeqStore ZSTDMT_rust_bufferToSeq(ZSTDMT_RustBuffer buffer);
|
||||
ZSTDMT_RustBuffer ZSTDMT_rust_seqToBuffer(ZSTDMT_RustRawSeqStore seq);
|
||||
typedef int (*ZSTDMT_serialWaitForTurnFn)(void* opaque, unsigned jobID);
|
||||
typedef void (*ZSTDMT_serialGenerateLdmFn)(
|
||||
void* opaque, ZSTDMT_RustRawSeqStore* seqStore,
|
||||
const void* src, size_t srcSize);
|
||||
typedef void (*ZSTDMT_serialUpdateChecksumFn)(
|
||||
void* opaque, const void* src, size_t srcSize);
|
||||
typedef void (*ZSTDMT_serialAdvanceFn)(void* opaque);
|
||||
void ZSTDMT_rust_serialStateGenSequences(
|
||||
ZSTDMT_RustRawSeqStore* seqStore, const void* src, size_t srcSize,
|
||||
unsigned jobID, int ldmEnabled, int checksumEnabled, void* opaque,
|
||||
ZSTDMT_serialWaitForTurnFn waitForTurn,
|
||||
ZSTDMT_serialGenerateLdmFn generateLdm,
|
||||
ZSTDMT_serialUpdateChecksumFn updateChecksum,
|
||||
ZSTDMT_serialAdvanceFn advance);
|
||||
|
||||
unsigned ZSTDMT_rust_computeTargetJobLog(unsigned windowLog, unsigned chainLog,
|
||||
int strategy, int enableLdm);
|
||||
@@ -862,44 +876,57 @@ static void ZSTDMT_serialState_free(SerialState* serialState)
|
||||
ZSTD_customFree(serialState->ldmState.bucketOffsets, cMem);
|
||||
}
|
||||
|
||||
static void
|
||||
ZSTDMT_serialState_genSequences(SerialState* serialState,
|
||||
RawSeqStore_t* seqStore,
|
||||
Range src, unsigned jobID)
|
||||
/* Rust owns the serial turn/skip decision and operation ordering. The wait
|
||||
* callback intentionally leaves the main serial mutex locked; the advance
|
||||
* callback releases it after Rust has performed the current turn's work. */
|
||||
static int ZSTDMT_serialState_waitForTurn(void* opaque, unsigned jobID)
|
||||
{
|
||||
/* Wait for our turn */
|
||||
SerialState* const serialState = (SerialState*)opaque;
|
||||
ZSTD_PTHREAD_MUTEX_LOCK(&serialState->mutex);
|
||||
while (serialState->nextJobID < jobID) {
|
||||
DEBUGLOG(5, "wait for serialState->cond");
|
||||
ZSTD_pthread_cond_wait(&serialState->cond, &serialState->mutex);
|
||||
}
|
||||
/* A future job may error and skip our job */
|
||||
if (serialState->nextJobID == jobID) {
|
||||
/* It is now our turn, do any processing necessary */
|
||||
if (serialState->params.ldmParams.enableLdm == ZSTD_ps_enable) {
|
||||
size_t error;
|
||||
DEBUGLOG(6, "ZSTDMT_serialState_genSequences: LDM update");
|
||||
assert(seqStore->seq != NULL && seqStore->pos == 0 &&
|
||||
seqStore->size == 0 && seqStore->capacity > 0);
|
||||
assert(src.size <= serialState->params.jobSize);
|
||||
ZSTD_window_update(&serialState->ldmState.window, src.start, src.size, /* forceNonContiguous */ 0);
|
||||
error = ZSTD_ldm_generateSequences(
|
||||
&serialState->ldmState, seqStore,
|
||||
&serialState->params.ldmParams, src.start, src.size);
|
||||
/* We provide a large enough buffer to never fail. */
|
||||
assert(!ZSTD_isError(error)); (void)error;
|
||||
/* Update ldmWindow to match the ldmState.window and signal the main
|
||||
* thread if it is waiting for a buffer.
|
||||
*/
|
||||
ZSTD_PTHREAD_MUTEX_LOCK(&serialState->ldmWindowMutex);
|
||||
serialState->ldmWindow = serialState->ldmState.window;
|
||||
ZSTD_pthread_cond_signal(&serialState->ldmWindowCond);
|
||||
ZSTD_pthread_mutex_unlock(&serialState->ldmWindowMutex);
|
||||
}
|
||||
if (serialState->params.fParams.checksumFlag && src.size > 0)
|
||||
XXH64_update(&serialState->xxhState, src.start, src.size);
|
||||
}
|
||||
/* Now it is the next jobs turn */
|
||||
return serialState->nextJobID == jobID;
|
||||
}
|
||||
|
||||
static void ZSTDMT_serialState_generateLdm(
|
||||
void* opaque, ZSTDMT_RustRawSeqStore* seqStore,
|
||||
const void* src, size_t srcSize)
|
||||
{
|
||||
SerialState* const serialState = (SerialState*)opaque;
|
||||
RawSeqStore_t* const cSeqStore = (RawSeqStore_t*)seqStore;
|
||||
size_t error;
|
||||
|
||||
DEBUGLOG(6, "ZSTDMT_serialState_genSequences: LDM update");
|
||||
assert(cSeqStore->seq != NULL && cSeqStore->pos == 0 &&
|
||||
cSeqStore->size == 0 && cSeqStore->capacity > 0);
|
||||
assert(srcSize <= serialState->params.jobSize);
|
||||
ZSTD_window_update(&serialState->ldmState.window, src, srcSize,
|
||||
/* forceNonContiguous */ 0);
|
||||
error = ZSTD_ldm_generateSequences(
|
||||
&serialState->ldmState, cSeqStore,
|
||||
&serialState->params.ldmParams, src, srcSize);
|
||||
/* We provide a large enough buffer to never fail. */
|
||||
assert(!ZSTD_isError(error)); (void)error;
|
||||
/* Update ldmWindow to match the ldmState.window and signal the main
|
||||
* thread if it is waiting for a buffer. */
|
||||
ZSTD_PTHREAD_MUTEX_LOCK(&serialState->ldmWindowMutex);
|
||||
serialState->ldmWindow = serialState->ldmState.window;
|
||||
ZSTD_pthread_cond_signal(&serialState->ldmWindowCond);
|
||||
ZSTD_pthread_mutex_unlock(&serialState->ldmWindowMutex);
|
||||
}
|
||||
|
||||
static void ZSTDMT_serialState_updateChecksum(
|
||||
void* opaque, const void* src, size_t srcSize)
|
||||
{
|
||||
SerialState* const serialState = (SerialState*)opaque;
|
||||
XXH64_update(&serialState->xxhState, src, srcSize);
|
||||
}
|
||||
|
||||
static void ZSTDMT_serialState_advance(void* opaque)
|
||||
{
|
||||
SerialState* const serialState = (SerialState*)opaque;
|
||||
serialState->nextJobID++;
|
||||
ZSTD_pthread_cond_broadcast(&serialState->cond);
|
||||
ZSTD_pthread_mutex_unlock(&serialState->mutex);
|
||||
@@ -1033,8 +1060,16 @@ static void ZSTDMT_compressionJobGenerateSequences(void* opaque)
|
||||
ZSTDMT_jobDescription* const job = state->job;
|
||||
|
||||
/* Perform serial step as early as possible. */
|
||||
ZSTDMT_serialState_genSequences(job->serial, &state->rawSeqStore,
|
||||
job->src, job->jobID);
|
||||
ZSTDMT_rust_serialStateGenSequences(
|
||||
(ZSTDMT_RustRawSeqStore*)&state->rawSeqStore,
|
||||
job->src.start, job->src.size, job->jobID,
|
||||
job->serial->params.ldmParams.enableLdm == ZSTD_ps_enable,
|
||||
job->serial->params.fParams.checksumFlag,
|
||||
job->serial,
|
||||
ZSTDMT_serialState_waitForTurn,
|
||||
ZSTDMT_serialState_generateLdm,
|
||||
ZSTDMT_serialState_updateChecksum,
|
||||
ZSTDMT_serialState_advance);
|
||||
}
|
||||
|
||||
static size_t ZSTDMT_compressionJobBegin(void* opaque)
|
||||
|
||||
Reference in New Issue
Block a user