Skip to content

Benchmarked performance improvements for FastMM5 #99

Description

@janrysavy

I profiled several FastMM5 allocation paths and found three small changes worth proposing. Each improved its target workload on both an AMD Ryzen 9 7950X and an Intel Core i7-8750H, although the magnitude varied by target. I am opening them as separate pull requests so they can be reviewed, tested, and merged independently. This issue collects the shared methodology, cross-CPU results, regression checks, and links.

Proposed pull requests

The two debug changes are independent and each PR reports its isolated result. A cumulative build was measured only to check their interaction; it is not a fourth source proposal. The realloc change affects only the Win64 assembler path; Win32 and PurePascal remain unchanged.

Result overview

CPU Proposal and workload Win64 gain Win32 gain
AMD Ryzen 9 7950X Debug fills, 4 KiB +52.36% +62.89%
Intel Core i7-8750H Debug fills, 4 KiB +104.58% +128.59%
AMD Ryzen 9 7950X Freed-pattern validation, 4 KiB +40.18% +14.45%
Intel Core i7-8750H Freed-pattern validation, 4 KiB +38.31% +3.30%
AMD Ryzen 9 7950X Win64 in-place small-block realloc, 100-pair confirmation +49.90% Not applicable
Intel Core i7-8750H Win64 in-place small-block realloc, 100-pair confirmation +14.65% Not applicable

These are targeted allocator microbenchmarks, not estimates of whole-application performance. The Intel Win32 validation result is included even though it is below 10% because it shows how much this optimization depends on CPU and architecture. The individual PRs include 64-byte and 64 KiB results, confidence intervals, and boundary or slow-path controls.

Benchmark methodology

I wrote this benchmark for the review; it is not part of an official FastMM5 benchmark suite. Source inspection and VTune runs over allocation, debug-fill, validation, reallocation, and arena-contention workloads identified the hot paths. VTune was used only to decide where to look. The published numbers come from a standalone QPC harness, with raw samples included so the results can be reproduced without a profiler.

FastMM5 keeps allocator state for the life of a process, so every baseline and candidate sample runs in a fresh process. This prevents either build from inheriting allocator lists, debug state, or arenas changed by the other. Each pair runs the same workload once with each build, and the order alternates between pairs to spread thermal, clock, scheduler, and background-load drift rather than consistently favoring the first or second executable.

Three unrecorded warmup pairs load executable and operating-system code pages and let CPU and system state settle; they do not precondition the allocator state of later samples. Operation counts make the timed section long compared with barrier and timer overhead. They were reduced for the slower Intel CPU to keep the full run practical, but the baseline and candidate always perform the same work within a pair, and the pair counts and analysis are identical.

Worker threads and synchronization objects are created before timing. QPC starts just before the start barrier is released and stops after all workers finish, excluding process and thread creation while covering the full allocator workload. Workers are pinned to distinct logical processors, and an affinity failure aborts the run. The process requests HIGH_PRIORITY_CLASS to reduce preemption noise, and a deterministic checksum catches any baseline/candidate work mismatch.

The main datasets use 25 measured pairs. Realloc confirmation and slow or non-target controls use 100 because effects near zero need more observations. Results are medians of the per-pair throughput gains rather than ratios of two separately aggregated medians. Pairing keeps each comparison local in time, while the median limits the effect of scheduler and background-work outliers. The 95% interval comes from a deterministic 10,000-resample bootstrap of that paired median, without assuming that timing ratios are normally distributed.

Each proposal is built and measured independently against the same baseline. A build containing both debug changes is included only to check their interaction. Separate measurements cover sizes around each new condition, realloc slow paths, and non-target medium and large blocks. Correctness programs are kept separate from timing and test corruption, pointer movement, data preservation, and policy boundaries. Running Win32 and Win64 on two different CPUs helps expose architecture-specific results, although it cannot represent every application or processor.

All proposals use FastMM5 master commit 2f0c127dc63d2f8954f65d4cab895236a2af4158 as their common base. The performance executables were built with Delphi Win32/Win64 37.0.59082.6021 and release-style -O+ optimization on Windows 11 build 26200 using the Balanced power plan. Throughput gain is (baseline time / candidate time - 1) * 100, so +100% means twice the throughput. The evidence package contains the benchmark source, operations, worker counts, pair order, raw rows, and analysis code.

Regression and compatibility coverage

Boundary and slow-path controls found only small costs: at most 2.12% for either isolated debug patch and 2.50% for realloc moving downsizes; the non-PR cumulative debug build reached 5.13% in one remainder-only case. Medium-block and large-block realloc controls were practically neutral. Dedicated debug-pattern and realloc tests passed with DCC 37 on Win32 and Win64 on both CPUs, with DCC 35/36 and PurePascal checks where applicable. Details are in the individual PRs and evidence package.

Reproduction material

The evidence package is available at https://github.com/janrysavy/FastMM5/tree/ed3229e678bbe4b2455b8435168442d51f430996/perf-review. It contains the baseline and candidate sources, standalone benchmark and correctness programs, build and run scripts, environment metadata, all AMD and Intel samples and summaries, candidate patches, SHA-256 manifests, and a validator that recomputes the published statistics without running the benchmarks.

Related arena finding

The same review found a large improvement from the existing FastMM_16Arenas option in a contention-heavy medium-block workload: 3.06x/3.07x throughput with 16 workers on AMD and 1.90x/1.81x with 12 workers on Intel for Win64/Win32. This does not justify changing the global default, so it is configuration evidence rather than a fourth source PR. I will keep that discussion in issue #41.

Scope

These proposals preserve FastMM5's public API and documented debug behavior. They do not change the default arena count, disable debug features, or claim general application-wide gains. PR #92 is complementary to the debug proposals: it can disable fill behavior for profiling-oriented configurations, while these changes preserve and accelerate the existing diagnostic behavior when it remains enabled.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions