On Mon, 27 Apr 2026 17:15:57 GMT, jengebr <[email protected]> wrote: >> # HashMap.putAll() optimizations >> >> ## Summary >> >> This PR adds fast paths to `HashMap.putMapEntries()` for `HashMap` and >> `Collections.UnmodifiableMap(HashMap)` sources. These deliver 66-86% >> improvement for the targeted cases with zero regression for other map types. >> >> ## Problem >> >> `HashMap.putMapEntries()` iterates via `entrySet()` using a chain of virtual >> calls: `entrySet()`, `iterator()`, `hasNext()`, `next()`, `getKey()`, >> `getValue()` and `hashCode()`. These call sites are inherently megamorphic >> because `putMapEntries()` accepts any Map implementation. The JIT cannot >> devirtualize them. `UnmodifiableMap` compounds the problem by adding a >> megamorphic wrapper layer on top. >> >> See JDK-8368292 for full analysis. >> >> ## Solution >> >> `putMapEntries()` checks whether the source is exactly `HashMap.class` or an >> `UnmodifiableMap` wrapping one. If so, it dispatches to >> `putHashMapEntries()` which walks the source `Node[]` table directly. This >> eliminates the megamorphic iteration call sites and reuses the pre-computed >> hash stored in each Node — avoiding redundant `hashCode()` calls on every >> key. >> >> An exact class identity check (not `instanceof`) is used for `HashMap` to >> prevent subclasses from entering the fast path. This avoids correctness >> issues with overridden behavior and keeps the fast path monomorphic. >> >> `UnmodifiableMap.m` visibility is changed from `private` to package-private. >> The class remains non-public and is only accessible within `java.util`. >> >> ## Performance >> >> Benchmarks use `BigInteger(128)` keys whose `hashCode()` is not cached. Call >> sites are poisoned with five Map types to simulate real-world megamorphic >> conditions. Platform: aarch64 (AWS Graviton). >> >> ### Optimized paths (poisoned call sites) >> >> >> Source │Size│Baseline (ns/op)│Optimized (ns/op)│Improvement >> ─────────────────────────┼────┼────────────────┼─────────────────┼─────────── >> HashMap │ 5 │ 202.1 ± 0.6 │ 78.0 ± 0.3 │ 61% >> HashMap │ 25 │ 906.4 ± 1.0│ 286.1 ± 2.5 │ 68% >> HashMap │150 │ 6654.2 ± 40.9│ 1539.1 ± 27.6 │ 77% >> UnmodifiableMap(HashMap) │ 5 │ 352.7 ± 0.6 │ 77.5 ± 1.0 │ 78% >> UnmodifiableMap(HashMap) │ 25 │ 1772.3 ± 5.6 │ 289.6 ± 2.5 │ 84% >> UnmodifiableMap(HashMap) │150 │ 10593.2 ± 37.5│ 1533.1 ± 20.4 │ 86% >> >> >> ### Non-optimized paths (poisoned, size=150) >> >> >> Source │Baseline (ns/op)│Optimized (ns/op)│Delta >> ... > > jengebr has updated the pull request incrementally with one additional commit > since the last revision: > > PR feedback on MOAT
@jengebr Your change (at version 13fac4cace7cfd2296ae96583aded000b82a994a) is now ready to be sponsored by a Committer. ------------- PR Comment: https://git.openjdk.org/jdk/pull/28243#issuecomment-4338480896
