Skip to content

Commit 21fd7cd

Browse files
committed
perf: Replace division with multiplication in adaptive chain depth calc
Eliminate integer division in the deflate hot path by transforming `(matches * 100 / searches) < threshold` to `(matches * 100) < (searches * threshold)`. Division is ~5-20x slower than multiplication on modern CPUs, and this code runs on every hash chain search in dflt(). Add 3 test cases covering all adaptive chain depth branches (low/medium/high success rate). Benchmark: all 68 benchmarks pass, 137 tests pass with no warnings.
1 parent 90720e9 commit 21fd7cd

2 files changed

Lines changed: 65 additions & 3 deletions

File tree

src/deflate.mbt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -399,10 +399,10 @@ fn dflt(
399399
let max_chain = if total_searches < 100 {
400400
base_chain
401401
} else {
402-
let success_rate = successful_matches * 100 / total_searches
403-
if success_rate < 5 {
402+
let success_100 = successful_matches * 100
403+
if success_100 < total_searches * 5 {
404404
4
405-
} else if success_rate < 20 {
405+
} else if success_100 < total_searches * 20 {
406406
8
407407
} else {
408408
base_chain

src/deflate_wbtest.mbt

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,3 +451,65 @@ test "deflate with dictionary" {
451451
assert_eq(decompressed[i], data[i])
452452
}
453453
}
454+
455+
///|
456+
test "adaptive chain depth - high entropy triggers low chain depth" {
457+
// Large random data triggers >100 hash searches with low success rate,
458+
// exercising the success_100 < total_searches * 5 branch (chain depth = 4)
459+
let size = 50000
460+
let data = FixedArray::make(size, b'\x00')
461+
let mut seed = 77777
462+
for i in 0..<size {
463+
seed = (seed * 1103515245 + 12345) & 0x7FFFFFFF
464+
data[i] = (seed % 256).to_byte()
465+
}
466+
let compressed = deflate_sync(data)
467+
let decompressed = inflate_sync(compressed)
468+
assert_eq(decompressed.length(), size)
469+
for i in 0..<size {
470+
assert_eq(decompressed[i], data[i])
471+
}
472+
}
473+
474+
///|
475+
test "adaptive chain depth - repetitive data triggers high chain depth" {
476+
// Highly repetitive data has high match success rate,
477+
// exercising the base_chain branch (success_100 >= total_searches * 20)
478+
let size = 50000
479+
let data = FixedArray::make(size, b'\x00')
480+
for i in 0..<size {
481+
data[i] = (i % 16).to_byte()
482+
}
483+
let compressed = deflate_sync(data)
484+
assert_true(compressed.length() < size / 2)
485+
let decompressed = inflate_sync(compressed)
486+
assert_eq(decompressed.length(), size)
487+
for i in 0..<size {
488+
assert_eq(decompressed[i], data[i])
489+
}
490+
}
491+
492+
///|
493+
test "adaptive chain depth - mixed data exercises medium chain depth" {
494+
// Data mixing compressible and random regions triggers medium success rate,
495+
// exercising the success_100 < total_searches * 20 branch (chain depth = 8)
496+
let size = 50000
497+
let data = FixedArray::make(size, b'\x00')
498+
let mut seed = 33333
499+
for i in 0..<size {
500+
if i % 200 < 100 {
501+
// Compressible region: repetitive pattern
502+
data[i] = (i % 8).to_byte()
503+
} else {
504+
// Random region
505+
seed = (seed * 1103515245 + 12345) & 0x7FFFFFFF
506+
data[i] = (seed % 256).to_byte()
507+
}
508+
}
509+
let compressed = deflate_sync(data)
510+
let decompressed = inflate_sync(compressed)
511+
assert_eq(decompressed.length(), size)
512+
for i in 0..<size {
513+
assert_eq(decompressed[i], data[i])
514+
}
515+
}

0 commit comments

Comments
 (0)