Skip to content

Use FillChar for debug fill patterns of 40 bytes and larger#96

Closed
janrysavy wants to merge 1 commit into
pleriche:masterfrom
janrysavy:perf/debug-fillchar
Closed

Use FillChar for debug fill patterns of 40 bytes and larger#96
janrysavy wants to merge 1 commit into
pleriche:masterfrom
janrysavy:perf/debug-fillchar

Conversation

@janrysavy

@janrysavy janrysavy commented Jul 21, 2026

Copy link
Copy Markdown

Summary

This adds a small FillChar fast path to FillFreedDebugBlockWithDebugPattern and FillAllocatedDebugBlockWithDebugPattern for user sizes of 40 bytes and larger. In the 4 KiB allocate/free benchmark, throughput improved by 52.36% to 62.89% on AMD and 104.58% to 128.59% on Intel across Win64 and Win32. Smaller blocks continue to use the original scalar code. For freed blocks, the code restores the TFastMM_FreedObject pointer after filling, preserving the existing debug-block layout and diagnostics. The diff adds 15 lines.

This is complementary to #92: that PR can disable fill patterns for profiling-oriented configurations, while this change speeds up the existing full diagnostic behavior when fill patterns remain enabled.

Tracking issue: #99

Benchmark

The benchmark repeatedly allocates and frees one block in FastMM5 debug mode with stack-trace collection disabled. Results are medians of 25 alternating baseline/candidate process pairs after three warmups. Throughput gain is (baseline time / candidate time - 1) * 100; the 95% interval is a deterministic 10,000-resample bootstrap interval for the paired median.

AMD Ryzen 9 7950X:

Target User size Operations Baseline median Candidate median Throughput gain (95% CI)
Win64 64 B 4,000,000 190.56 ms 175.34 ms 8.96% (8.46% to 10.13%)
Win32 64 B 4,000,000 171.22 ms 174.36 ms -1.62% (-3.20% to -1.07%)
Win64 4 KiB 750,000 337.03 ms 221.28 ms 52.36% (52.16% to 52.49%)
Win32 4 KiB 750,000 358.80 ms 221.28 ms 62.89% (51.07% to 65.11%)
Win64 64 KiB 50,000 311.22 ms 192.02 ms 61.87% (61.72% to 62.03%)
Win32 64 KiB 50,000 331.08 ms 192.58 ms 71.96% (71.59% to 73.39%)

Intel Core i7-8750H:

Target User size Operations Baseline median Candidate median Throughput gain (95% CI)
Win64 64 B 2,100,000 205.62 ms 185.75 ms 10.55% (9.41% to 11.92%)
Win32 64 B 2,000,000 174.79 ms 161.55 ms 8.17% (7.65% to 8.94%)
Win64 4 KiB 268,000 310.53 ms 151.43 ms 104.58% (103.13% to 105.44%)
Win32 4 KiB 286,000 365.08 ms 159.67 ms 128.59% (125.41% to 129.47%)
Win64 64 KiB 17,000 291.05 ms 137.29 ms 112.54% (110.16% to 114.24%)
Win32 64 KiB 18,000 337.73 ms 142.56 ms 136.63% (134.74% to 137.90%)

The measured 4 KiB and 64 KiB cases improved substantially; the 64-byte controls were near the crossover, ranging from a 1.62% loss to a 10.55% gain. Additional 25-pair tests cover 1, 39, 40, and 41 bytes on both architectures.

The worst AMD result was -1.89% at one byte on Win32, with all Win64 controls positive. On Intel, the worst results were -1.62% at 39 bytes on Win32 (CI -2.37% to -0.08%) and -1.27% at 39 bytes on Win64 (CI -2.26% to -0.33%). Avoiding those small boundary losses would require duplicating or restructuring the scalar loops, which does not seem justified for debug mode.

Tests ran on an AMD Ryzen 9 7950X and an Intel Core i7-8750H under Windows 11 build 26200, using Delphi Win32/Win64 37.0.59082.6021 with release-style -O+ builds. The Intel runs use fewer operations to keep process duration similar; both builds still perform identical work within each pair, and the pairing and analysis are unchanged.

Correctness

FastMMDebugPatternTest.dpr passes for the DCC 37 baseline and candidate on Win32 and Win64 on both CPUs. Additional compatibility runs pass with DCC 35 and 36 Win32/Win64 and with DCC 37 PurePascal. The test verifies every allocated fill byte for sizes 1 through 1025, then corrupts and restores every byte for freed-block sizes 1 through 65 and 257. It accepts only the expected EInvalidPointer and requires a clean scan afterward, covering the object marker and every baseline 8/4/2/1-byte remainder path.

Full benchmark source, raw AMD and Intel samples, summaries, and correctness tests: https://github.com/janrysavy/FastMM5/tree/ed3229e678bbe4b2455b8435168442d51f430996/perf-review

Compatibility note

FastMM5 supports Delphi versions older than the DCC 35 through 37 compilers used here. FillChar is suitable for this code and does not allocate. The original scalar path remains below 40 bytes, but the crossover should still be checked with the oldest supported RTL.

@janrysavy
janrysavy marked this pull request as ready for review July 21, 2026 09:44
@pleriche

Copy link
Copy Markdown
Owner

Thanks Jan, I've implemented your suggestion. I have wrapped it in a "{$if CompilerVersion >= 28}" (XE7), because IIRC that is where FillChar saw a significant improvement. (Please let me know if I am wrong.)

@janrysavy

Copy link
Copy Markdown
Author

Pierre, could you please reference the original PRs or issues in your commits so there’s a clear link back for future reference?

@janrysavy janrysavy closed this Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants