Skip to content

Avoid intermediate Map allocations and tuple boxing in groupFlatMap#79

Open
cheeseng wants to merge 1 commit into
dotty-staging:stdlib/group-flat-mapfrom
artimahub:stdlib/group-flat-map-artima-cs
Open

Avoid intermediate Map allocations and tuple boxing in groupFlatMap#79
cheeseng wants to merge 1 commit into
dotty-staging:stdlib/group-flat-mapfrom
artimahub:stdlib/group-flat-map-artima-cs

Conversation

@cheeseng

@cheeseng cheeseng commented Jul 11, 2026

Copy link
Copy Markdown

The Result/AbstractFunction1 pattern finalized builders via repeated
calls to immutable.Map#updated, which rebuilds trie nodes on every
call and discards all but the final version as garbage.

Replace it with a single Map builder populated via foreachEntry:

  val b = immutable.Map.newBuilder[K, CC[B]]
  m.foreachEntry((k, bldr) => b += ((k, bldr.result())))
  b.result()

This avoids both problems in one pass: results are staged once and
frozen once (no repeated .updated trie rebuilds), and foreachEntry
traverses m's internal table directly instead of going through
m.iterator, so no (K, Builder) pair is boxed per entry just to be
immediately unpacked again (as view.mapValues(_.result()).toMap
would do). Same O(n + k) complexity (n = elements, k = groups),
fewer allocations on both counts.

Enhanced the test in group-flat-map.scala to cover more cases.

The Result/AbstractFunction1 pattern finalized builders via repeated
calls to immutable.Map#updated, which rebuilds trie nodes on every
call and discards all but the final version as garbage.

Replace it with a single Map builder populated via foreachEntry:

  val b = immutable.Map.newBuilder[K, CC[B]]
  m.foreachEntry((k, bldr) => b += ((k, bldr.result())))
  b.result()

This avoids both problems in one pass: results are staged once and
frozen once (no repeated .updated trie rebuilds), and foreachEntry
traverses m's internal table directly instead of going through
m.iterator, so no (K, Builder) pair is boxed per entry just to be
immediately unpacked again (as view.mapValues(_.result()).toMap
would do). Same O(n + k) complexity (n = elements, k = groups),
fewer allocations on both counts.

Also enhance tests in group-flat-map.scala to cover more cases:
duplicate keys from expansion, cross-element duplicates within a
group, mixed expansion sizes, and empty-expansion groups.
@cheeseng
cheeseng force-pushed the stdlib/group-flat-map-artima-cs branch from 184dbf5 to 3b327ae Compare July 20, 2026 09:20
@cheeseng cheeseng changed the title Avoid intermediate Map allocations in groupFlatMap Avoid intermediate Map allocations and tuple boxing in groupFlatMap Jul 20, 2026
@cheeseng

Copy link
Copy Markdown
Author

@bishabosha I tried to reproduce the problem by running the same command locally but it didn't fail, can you try rerun the failed CI?

Thanks.

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.

1 participant