XPerf
LanguagesRubyEvidence_removed

rb-hash-merge-in-loop — Hash#merge in loop vs build-once

Archived tombstone. The matcher is gone. See rb-hash-merge-in-loop.REMOVED.md. The measurement note below is the old record.

Claim

Prior “good” was fix-wrong. Fixture base.dup + merge! was slower than base.merge(...) (~0.60–0.85×) because merge already allocates one hash and dup+merge! allocates two. Recommended fix (2026-10-22 land): build once via Hash literal / assign (no blind dup). On Ruby 4.0.8 the wall-clock ratio reversed: merge is faster than the literal (0.784×, 6.228 ms vs 7.947 ms, N=50000). A second pass the same evening, load average about 24, still had merge faster on both medians (0.645× and 0.655×). Samples were noisy; the direction was not. Default stays hint. Escalate recommended: no.

How measured

  • Harness: packages/rubyperf/examples/bench/evidence_rb.rb (after arm = hash_literal_build)
  • Pair A: N=50000 Sidekiq-ish job tagging
  • Pair B: N=100 × 200 reps
  • Raw: rb-hash-merge-in-loop.jsonl

Runtime (this evidence file)

FieldValue
When2026-10-23 Asia/Shanghai
Ruby4.0.8
Median before (hash_merge)6.228 ms
Median after (hash_literal_build)7.947 ms
ratio (before/after)0.784× (merge faster)
Historical wrong-good (dup_merge_bang)~0.60× at N=50k (after slower)

Lint mapping

Keep hint. Good fixture rubyperf-lint/fixtures/good/rb-hash-merge-in-loop_merge_bang.rb now builds via literal (filename kept for smoke stability).

Limits

  • Microbench; merge! on a hash you already uniquely own (no dup) remains a valid mutate pattern — that is not what the old fixture measured.
  • Memory: see rb-hash-merge-in-loop.mem.jsonl.

Landed 2026-10-22

fix-wrong: message + fixture + harness after-arm rewritten; wall re-bench ~1.12×. Severity unchanged.

Memory (2026-10-22)

Ruby 4.0.8, N=50000: hash_merge 100005 allocated objects, dup_merge_bang 100005, hash_literal_build 200001. Retained Hash memsize_of 8000000 bytes for all three. The literal now allocates about twice as many objects as merge, and the wall-clock ratio is 0.784×. Raw: rb-hash-merge-in-loop.mem.jsonl.