NAME

bench_retention.pl - Compare encoder traversal time and peak memory

REPRODUCING THE COMPARISON

Build each revision with the same Perl and compiler flags. From its Perl/Encoder directory, run this script with the local build loaded:

PERL_HASH_SEED=0 PERL_PERTURB_KEYS=0 /usr/bin/time -f 'peak_rss_kb=%M' \
    perl -Mblib author_tools/bench_retention.pl \
    --case records --count 200000 --iterations 9

Use --case arrays --count 300000 for the single-element-array case. The script also supplies flat scalars and unique hash keys cases. Run the revisions sequentially to avoid competing benchmark processes. Use --protocol-version 5 when comparing with an installed v5 release; the default remains protocol version 6.

Sample measurements on Linux x86-64, Perl 5.44.0, GCC 13, -O3, with nine timed encodes after one warm-up encode:

                     Median milliseconds             Peak RSS (KiB)
Input             historical  before    scan      historical before  scan
200k records         40.664   731.681   59.198       315028   501892 262676
300k arrays           7.460    40.602   10.873        79128   101116  81404
200k scalars          1.419     1.471    1.711        25056    25092  26636
300k keys           118.293   145.145  133.217       154080   158704 154152

historical is d6d5378d, before container retention; before is 99927aad, with unconditional retention for nested references; scan is the complete input scan that selects retention only when Perl callbacks are possible. Peak RSS includes input construction, encoder state and the returned packet. Measurements depend on the machine and allocator.

That historical scan removed most of the nested-container regression, but still added work compared with the historical encoder, including for flat scalars. That implementation retained input graphs containing magic, objects or unsupported types.

The current encoder instead guards actual callback boundaries in a single traversal. It keeps borrowed state alive long enough to detect unsafe mutation and throw, without a whole-graph pre-scan or container-value snapshots.