feat(mt): move compression job creation policy into Rust
Move the multithreaded compression job-creation decision tree into a Rust projection while keeping C-owned job descriptors, input buffers, pools, synchronization, worker callbacks, and terminal empty-block serialization behind explicit callbacks. The Rust policy now preserves ring-table full checks, prepared-job retry state, prefix advancement, frame checksum rules, and the non-first empty terminal job path without crossing private C layouts. Test Plan: - cargo test --manifest-path rust/Cargo.toml --all-targets -- --test-threads=1 - cargo test --manifest-path rust/Cargo.toml --no-default-features --features compression,decompression,dict-builder,legacy-v01,legacy-v02,legacy-v03,legacy-v04,legacy-v05,legacy-v06,legacy-v07 --all-targets -- --test-threads=1 - cargo clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings - make -B -C lib -j2 lib - make -B -C tests -j2 test-zstd test-pool test-legacy test-invalidDictionaries
This commit is contained in:
+142
-66
@@ -202,6 +202,56 @@ ZSTDMT_RustEmptyBlockResult ZSTDMT_rust_writeLastEmptyBlock(
|
||||
const ZSTDMT_RustEmptyBlockJobProjection* projection,
|
||||
void* opaque, ZSTDMT_bufferGetFn getBuffer);
|
||||
|
||||
typedef struct {
|
||||
unsigned doneJobID;
|
||||
unsigned nextJobID;
|
||||
unsigned jobIDMask;
|
||||
unsigned jobReady;
|
||||
const void* srcStart;
|
||||
size_t srcSize;
|
||||
size_t inBuffFilled;
|
||||
const void* prefixStart;
|
||||
size_t prefixSize;
|
||||
size_t targetPrefixSize;
|
||||
unsigned endFrame;
|
||||
unsigned checksumFlag;
|
||||
} ZSTDMT_RustCreateJobProjection;
|
||||
typedef struct {
|
||||
const void* srcStart;
|
||||
size_t srcSize;
|
||||
const void* prefixStart;
|
||||
size_t prefixSize;
|
||||
const void* nextPrefixStart;
|
||||
size_t nextPrefixSize;
|
||||
size_t roundBuffPosDelta;
|
||||
unsigned jobNumber;
|
||||
unsigned firstJob;
|
||||
unsigned lastJob;
|
||||
unsigned frameChecksumNeeded;
|
||||
unsigned clearChecksumFlag;
|
||||
} ZSTDMT_RustJobInitialization;
|
||||
typedef struct {
|
||||
size_t returnCode;
|
||||
unsigned action;
|
||||
unsigned jobID;
|
||||
unsigned jobNumber;
|
||||
unsigned nextJobID;
|
||||
unsigned jobReady;
|
||||
} ZSTDMT_RustCreateJobResult;
|
||||
enum {
|
||||
ZSTDMT_CREATE_JOB_TABLE_FULL = 0,
|
||||
ZSTDMT_CREATE_JOB_POST = 1,
|
||||
ZSTDMT_CREATE_JOB_EMPTY = 2
|
||||
};
|
||||
typedef void (*ZSTDMT_prepareJobFn)(void* opaque, unsigned jobID,
|
||||
const ZSTDMT_RustJobInitialization* init);
|
||||
typedef void (*ZSTDMT_writeEmptyJobFn)(void* opaque, unsigned jobID);
|
||||
typedef int (*ZSTDMT_tryAddJobFn)(void* opaque, unsigned jobID);
|
||||
ZSTDMT_RustCreateJobResult ZSTDMT_rust_createCompressionJob(
|
||||
const ZSTDMT_RustCreateJobProjection* projection,
|
||||
void* opaque, ZSTDMT_prepareJobFn prepareJob,
|
||||
ZSTDMT_writeEmptyJobFn writeEmptyJob, ZSTDMT_tryAddJobFn tryAddJob);
|
||||
|
||||
typedef struct {
|
||||
rawSeq* seq;
|
||||
size_t pos;
|
||||
@@ -1477,82 +1527,108 @@ static void ZSTDMT_writeLastEmptyBlock(ZSTDMT_jobDescription* job)
|
||||
assert(!ZSTD_isError(job->cSize));
|
||||
}
|
||||
|
||||
static void ZSTDMT_prepareCompressionJob(
|
||||
void* opaque, unsigned jobID,
|
||||
const ZSTDMT_RustJobInitialization* initialization)
|
||||
{
|
||||
ZSTDMT_CCtx* const mtctx = (ZSTDMT_CCtx*)opaque;
|
||||
ZSTDMT_jobDescription* const job = &mtctx->jobs[jobID];
|
||||
BYTE const* const src = (const BYTE*)initialization->srcStart;
|
||||
|
||||
DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ",
|
||||
initialization->jobNumber, (U32)initialization->srcSize,
|
||||
(U32)initialization->prefixSize);
|
||||
assert(mtctx->inBuff.filled >= initialization->srcSize);
|
||||
job->src = (Range){ src, initialization->srcSize };
|
||||
job->prefix = (Range){
|
||||
(const BYTE*)initialization->prefixStart, initialization->prefixSize
|
||||
};
|
||||
job->consumed = 0;
|
||||
job->cSize = 0;
|
||||
job->params = mtctx->params;
|
||||
job->cdict = initialization->firstJob ? mtctx->cdict : NULL;
|
||||
job->fullFrameSize = mtctx->frameContentSize;
|
||||
job->dstBuff = g_nullBuffer;
|
||||
job->cctxPool = mtctx->cctxPool;
|
||||
job->bufPool = mtctx->bufPool;
|
||||
job->seqPool = mtctx->seqPool;
|
||||
job->serial = &mtctx->serial;
|
||||
job->jobID = initialization->jobNumber;
|
||||
job->firstJob = initialization->firstJob;
|
||||
job->lastJob = initialization->lastJob;
|
||||
job->frameChecksumNeeded = initialization->frameChecksumNeeded;
|
||||
job->dstFlushed = 0;
|
||||
|
||||
/* Update the round buffer position and clear the input buffer to be reset. */
|
||||
mtctx->roundBuff.pos += initialization->roundBuffPosDelta;
|
||||
mtctx->inBuff.buffer = g_nullBuffer;
|
||||
mtctx->inBuff.filled = 0;
|
||||
mtctx->inBuff.prefix = (Range){
|
||||
(const BYTE*)initialization->nextPrefixStart,
|
||||
initialization->nextPrefixSize
|
||||
};
|
||||
if (initialization->lastJob) {
|
||||
mtctx->frameEnded = 1;
|
||||
if (initialization->clearChecksumFlag)
|
||||
mtctx->params.fParams.checksumFlag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void ZSTDMT_writeEmptyCompressionJob(void* opaque, unsigned jobID)
|
||||
{
|
||||
ZSTDMT_CCtx* const mtctx = (ZSTDMT_CCtx*)opaque;
|
||||
ZSTDMT_writeLastEmptyBlock(&mtctx->jobs[jobID]);
|
||||
}
|
||||
|
||||
static int ZSTDMT_tryAddCompressionJob(void* opaque, unsigned jobID)
|
||||
{
|
||||
ZSTDMT_CCtx* const mtctx = (ZSTDMT_CCtx*)opaque;
|
||||
return POOL_tryAdd(mtctx->factory, ZSTDMT_compressionJob, &mtctx->jobs[jobID]);
|
||||
}
|
||||
|
||||
static size_t ZSTDMT_createCompressionJob(ZSTDMT_CCtx* mtctx, size_t srcSize, ZSTD_EndDirective endOp)
|
||||
{
|
||||
unsigned const jobID = mtctx->nextJobID & mtctx->jobIDMask;
|
||||
int const endFrame = (endOp == ZSTD_e_end);
|
||||
ZSTDMT_RustCreateJobProjection const projection = {
|
||||
mtctx->doneJobID,
|
||||
mtctx->nextJobID,
|
||||
mtctx->jobIDMask,
|
||||
mtctx->jobReady,
|
||||
mtctx->inBuff.buffer.start,
|
||||
srcSize,
|
||||
mtctx->inBuff.filled,
|
||||
mtctx->inBuff.prefix.start,
|
||||
mtctx->inBuff.prefix.size,
|
||||
mtctx->targetPrefixSize,
|
||||
(unsigned)(endOp == ZSTD_e_end),
|
||||
(unsigned)mtctx->params.fParams.checksumFlag
|
||||
};
|
||||
ZSTDMT_RustCreateJobResult const result = ZSTDMT_rust_createCompressionJob(
|
||||
&projection, mtctx, ZSTDMT_prepareCompressionJob,
|
||||
ZSTDMT_writeEmptyCompressionJob, ZSTDMT_tryAddCompressionJob);
|
||||
|
||||
if (mtctx->nextJobID > mtctx->doneJobID + mtctx->jobIDMask) {
|
||||
if (result.action == ZSTDMT_CREATE_JOB_TABLE_FULL) {
|
||||
DEBUGLOG(5, "ZSTDMT_createCompressionJob: will not create new job : table is full");
|
||||
assert((mtctx->nextJobID & mtctx->jobIDMask) == (mtctx->doneJobID & mtctx->jobIDMask));
|
||||
return 0;
|
||||
return result.returnCode;
|
||||
}
|
||||
|
||||
if (!mtctx->jobReady) {
|
||||
BYTE const* src = (BYTE const*)mtctx->inBuff.buffer.start;
|
||||
DEBUGLOG(5, "ZSTDMT_createCompressionJob: preparing job %u to compress %u bytes with %u preload ",
|
||||
mtctx->nextJobID, (U32)srcSize, (U32)mtctx->inBuff.prefix.size);
|
||||
mtctx->jobs[jobID].src.start = src;
|
||||
mtctx->jobs[jobID].src.size = srcSize;
|
||||
assert(mtctx->inBuff.filled >= srcSize);
|
||||
mtctx->jobs[jobID].prefix = mtctx->inBuff.prefix;
|
||||
mtctx->jobs[jobID].consumed = 0;
|
||||
mtctx->jobs[jobID].cSize = 0;
|
||||
mtctx->jobs[jobID].params = mtctx->params;
|
||||
mtctx->jobs[jobID].cdict = mtctx->nextJobID==0 ? mtctx->cdict : NULL;
|
||||
mtctx->jobs[jobID].fullFrameSize = mtctx->frameContentSize;
|
||||
mtctx->jobs[jobID].dstBuff = g_nullBuffer;
|
||||
mtctx->jobs[jobID].cctxPool = mtctx->cctxPool;
|
||||
mtctx->jobs[jobID].bufPool = mtctx->bufPool;
|
||||
mtctx->jobs[jobID].seqPool = mtctx->seqPool;
|
||||
mtctx->jobs[jobID].serial = &mtctx->serial;
|
||||
mtctx->jobs[jobID].jobID = mtctx->nextJobID;
|
||||
mtctx->jobs[jobID].firstJob = (mtctx->nextJobID==0);
|
||||
mtctx->jobs[jobID].lastJob = endFrame;
|
||||
mtctx->jobs[jobID].frameChecksumNeeded = mtctx->params.fParams.checksumFlag && endFrame && (mtctx->nextJobID>0);
|
||||
mtctx->jobs[jobID].dstFlushed = 0;
|
||||
|
||||
/* Update the round buffer pos and clear the input buffer to be reset */
|
||||
mtctx->roundBuff.pos += srcSize;
|
||||
mtctx->inBuff.buffer = g_nullBuffer;
|
||||
mtctx->inBuff.filled = 0;
|
||||
/* Set the prefix for next job */
|
||||
if (!endFrame) {
|
||||
size_t const newPrefixSize = MIN(srcSize, mtctx->targetPrefixSize);
|
||||
mtctx->inBuff.prefix.start = src + srcSize - newPrefixSize;
|
||||
mtctx->inBuff.prefix.size = newPrefixSize;
|
||||
} else { /* endFrame==1 => no need for another input buffer */
|
||||
mtctx->inBuff.prefix = kNullRange;
|
||||
mtctx->frameEnded = endFrame;
|
||||
if (mtctx->nextJobID == 0) {
|
||||
/* single job exception : checksum is already calculated directly within worker thread */
|
||||
mtctx->params.fParams.checksumFlag = 0;
|
||||
} }
|
||||
|
||||
if ( (srcSize == 0)
|
||||
&& (mtctx->nextJobID>0)/*single job must also write frame header*/ ) {
|
||||
DEBUGLOG(5, "ZSTDMT_createCompressionJob: creating a last empty block to end frame");
|
||||
assert(endOp == ZSTD_e_end); /* only possible case : need to end the frame with an empty last block */
|
||||
ZSTDMT_writeLastEmptyBlock(mtctx->jobs + jobID);
|
||||
mtctx->nextJobID++;
|
||||
return 0;
|
||||
}
|
||||
mtctx->nextJobID = result.nextJobID;
|
||||
mtctx->jobReady = result.jobReady;
|
||||
if (result.action == ZSTDMT_CREATE_JOB_EMPTY) {
|
||||
DEBUGLOG(5, "ZSTDMT_createCompressionJob: creating a last empty block to end frame");
|
||||
assert(endOp == ZSTD_e_end); /* only possible case : need to end the frame with an empty last block */
|
||||
return result.returnCode;
|
||||
}
|
||||
|
||||
DEBUGLOG(5, "ZSTDMT_createCompressionJob: posting job %u : %u bytes (end:%u, jobNb == %u (mod:%u))",
|
||||
mtctx->nextJobID,
|
||||
(U32)mtctx->jobs[jobID].src.size,
|
||||
mtctx->jobs[jobID].lastJob,
|
||||
mtctx->nextJobID,
|
||||
jobID);
|
||||
if (POOL_tryAdd(mtctx->factory, ZSTDMT_compressionJob, &mtctx->jobs[jobID])) {
|
||||
mtctx->nextJobID++;
|
||||
mtctx->jobReady = 0;
|
||||
} else {
|
||||
DEBUGLOG(5, "ZSTDMT_createCompressionJob: no worker available for job %u", mtctx->nextJobID);
|
||||
mtctx->jobReady = 1;
|
||||
}
|
||||
return 0;
|
||||
result.jobNumber,
|
||||
(U32)mtctx->jobs[result.jobID].src.size,
|
||||
mtctx->jobs[result.jobID].lastJob,
|
||||
result.jobNumber,
|
||||
result.jobID);
|
||||
if (result.jobReady)
|
||||
DEBUGLOG(5, "ZSTDMT_createCompressionJob: no worker available for job %u", result.jobNumber);
|
||||
return result.returnCode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user