Skip to content

Avoid redundant HashMap lookup in IngredientMapWrappedAdapter.iterator() - #232

Open
BlaiseBaptist wants to merge 1 commit into
CyclopsMC:master-26-ltsfrom
BlaiseBaptist:fix/ingredient-map-wrapped-adapter-redundant-lookup
Open

Avoid redundant HashMap lookup in IngredientMapWrappedAdapter.iterator()#232
BlaiseBaptist wants to merge 1 commit into
CyclopsMC:master-26-ltsfrom
BlaiseBaptist:fix/ingredient-map-wrapped-adapter-redundant-lookup

Conversation

@BlaiseBaptist

@BlaiseBaptist BlaiseBaptist commented Aug 14, 2026

Copy link
Copy Markdown

What

IngredientMapWrappedAdapter.iterator() iterates collection.keySet() and then calls collection.get(key) for every key it already has in hand from the iterator - a redundant second hash lookup per element. This PR switches it to iterate collection.entrySet() directly, which yields the same (key, value) pairs without the extra lookup. Behavior is unchanged.

Why this matters

On a large ingredient map containing many distinct data-component/NBT variants of the same item (enchanted books, AE2 storage cells, backpacks, etc.), those variants collide into the same hash bucket (item hash codes intentionally exclude components/NBT for performance elsewhere). Once a bucket treeifies, the redundant get() triggers a full equals() chain per element: DataComparator.compare -> PatchedDataComponentMap.equals -> component-by-component comparison. That cost is paid twice per element on every iteration because of the redundant lookup.

I ran into this via Integrated Terminals, where this iterator sits on the hot path of TerminalStorageTabIngredientComponentClient for building/diffing a storage terminal's ingredient view. On a large, diverse storage network (Sophisticated Storage + AE2 + Refined Storage combined), dragging an item out of a terminal (which keeps a slot actively selected while network-change packets stream in) triggered repeated full rebuilds of this view, pinning the client render thread at 300%+ CPU for a sustained freeze (confirmed via jstack thread dump - happy to share the full stack if useful).

This looks related in spirit to CyclopsMC/IntegratedTerminals#139 and #209, though it's a distinct code path from both - it's the "unnecessary duplicate lookup" issue independent of the update-batching/Sophisticated-Storage-specific work done for those.

Testing

  • Verified the fix eliminates the freeze in the field, via an equivalent Mixin-based patch of this exact method (same replacement logic) applied client-side against production CyclopsCore 1.29.2 / IntegratedTerminals 1.7.0-800 / NeoForge 21.1.248 - confirmed no more freeze when dragging items out of a large storage terminal, no behavior differences observed.
  • This PR applies the same change directly to source rather than shipping it as an external patch.

Disclosure

Root-cause analysis, the fix, and this PR description were produced with AI assistance (Claude Code), based on a real client-side freeze I hit and profiled myself (jstack thread dump referenced above). I reviewed and verified the change before submitting.

iterator() called collection.get(key) for every key already yielded by
collection.keySet().iterator(), a wasted second hash lookup for each
element. On a large ingredient map with many distinct data-component
variants of the same item, that lookup falls into treeified hash
buckets and pays for a full equals() chain (DataComparator ->
PatchedDataComponentMap.equals -> component-by-component comparison)
per element, per collection iteration.

In Integrated Terminals, this iterator is on the hot path for
rendering/diffing a storage terminal's ingredient view. On a large,
diverse network, iterating it while a slot is actively selected
(e.g. while dragging an item) can pin the client render thread at
100%+ CPU for tens of seconds, appearing as a full freeze.

Iterating entrySet() directly yields the same (key, value) pairs
without the redundant lookup.
@CLAassistant

CLAassistant commented Aug 14, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@sonarqubecloud

Copy link
Copy Markdown

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