Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SI-7149 Use a WeakHashSet for type uniqueness #2605

Merged
merged 3 commits into from Jun 4, 2013

Conversation

JamesIry
Copy link
Contributor

We have an implementation of WeakHashSet in the compiler but it never cleans up its WeakReferences. The first commit in this PR replaces the implementation with one that does clean up. Along the way it modifies the interface to follow the standard scala.collection.mutable.Set interface so that it can eventually be moved into the main library should we choose to do so.

Currently type uniqueness is done via a HashSet[Type], but
that means the Types live through an entire compile session, even
ones that are used once. The result is a huge amount of unnecessarily
retained memory. This commit uses a WeakHashSet instead so that Types
and their WeakReferences are cleaned up when no longer in use.

Finally, perRunCaches is also updated to use the WeakHashSet instead of a HashSet of WeakReferences because the former was never cleaning up unused WeakReferences.

@JamesIry
Copy link
Contributor Author

Review @gkossakowski.

@ghost ghost assigned gkossakowski May 28, 2013
@JamesIry
Copy link
Contributor Author

Cleaned up several typos in comments.

@gkossakowski
Copy link
Member

Currently type uniqueness is done via a HashSet of WeakReferences, but
that means the WeakReferences live through an entire compile session.
Another commit uses the WeakHashSet instead so that the WeakReferences are
cleaned up when no longer in use.

I thought we were not using any form of WeakReferences in uniques set thus we were accumulating those types for the entire compiler run. If you look at the diff in 4eb4aa5 you'll see that there are no WeakReferences involved there. Am I missing something?

@JamesIry
Copy link
Contributor Author

JamesIry commented Jun 3, 2013

You're right, my commit message is wrong.

@gkossakowski
Copy link
Member

@JamesIry: could you update the commit and PR message?

James Iry added 3 commits June 4, 2013 08:17
Replaces scala.reflect.internal.WeakHashSet with a version that
* extends the mutable.Set trait
* doesn't leak WeakReferences
* is unit tested
Currently type uniqueness is done via a HashSet[Type], but
that means the Types live through an entire compile session, even
ones that are used once. The result is a huge amount of unnecessarily
retained memory. This commit uses a WeakHashSet instead so that Types
and their WeakReferences are cleaned up when no longer in use.
perRunCaches was using a HashMap of WeakReferences which meant it would
accumulate WeakReferences over time. This commit uses a WeakHashSet
instead so that the references are cleaned up.
@JamesIry
Copy link
Contributor Author

JamesIry commented Jun 4, 2013

Cleaned up the commit message and added a comment to the WeakHashSet's hash avalanche mechanism.

@gkossakowski
Copy link
Member

LGTM.

Great work!

I did memory profiling of this change. I'm comparing b88801f (parent commit of this PR) and 29babb5 (last commit in this PR). I used YourKit and custom probe to collect memory snapshot after different phases. See YourKit documentation for detailed explanation of probes.

I compiled Scala library with probes enabled like this:

ant -Djvm.opts="-agentpath:/Applications/YourKit.app/bin/mac/libyjpagent.jnilib=builtinprobes=none,probeclasspath=/Users/grek/scala/scala-master/probe-classes,probe=MemoryProbe" quick.lib

Now the numbers (for compiling the Scala library)

Total retained memory after phase (in mb):
=========================================
Phase       Before      After   Lost
typer       173         136     37 (21%)
erasure     264         178     86 (32%)
cleanup     314         225     89 (28%)

Number of retained :: objects after each phase (in k):
=====================================================
Phase       Before      After   Lost
typer       808         502     306 (37%)
erasure     1432        766     666 (46%)
cleanup     1737        1044    693 (39%)

Number of retained TypeHistory objects after each phase (in k):
==============================================================
Phase       Before      After   Lost
typer       202         123     79  (39%)
erasure     473         259     214 (45%)
cleanup     605         378     227 (37%)


Number of retained ClassArgsTypeRef objects after each phase (in k):
===================================================================
Phase       Before      After   Lost
typer       226         88      138 (61%)
erasure     396         78      318 (80%)
cleanup     419         88      331 (78%)

Great work, again!

gkossakowski added a commit that referenced this pull request Jun 4, 2013
SI-7149 Use a WeakHashSet for type uniqueness
@gkossakowski gkossakowski merged commit 803d451 into scala:master Jun 4, 2013
@xeno-by
Copy link
Member

xeno-by commented Jun 4, 2013

You got to be kidding me!!

@viktorklang
Copy link
Contributor

FANTASTIC

@ihji
Copy link
Contributor

ihji commented Jun 5, 2013

Wow, the benchmark result is TOTALLY awesome. Great work!

@fernandoracca
Copy link

excellent job!

@jrudolph
Copy link
Member

jrudolph commented Jun 5, 2013

👍

/** A bare-bones implementation of a mutable `Set` that uses weak references
* to hold the elements.
/**
* A HashSet where the elements are stored weakly. Elements in this set are elligible for GC if no other

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: eligible

gkossakowski added a commit that referenced this pull request Sep 4, 2013
Backport #2605 to 2.10.x: SI-7149 Use a WeakHashSet for type uniqueness
smarter added a commit to dotty-staging/dotty that referenced this pull request Jun 25, 2021
This mimics what Scala 2 has been doing for a long time now and serves
the same purpose: it considerably reduces peak memory usage when
compiling some projects, for example previously compiling the Scalatest
tests required a heap of at least 11 GB, but now it fits in about 4 GB.

This required changing the implementation of WeakHashSet to have
overridable `hash` and `isEqual` methods just like HashSet, it also
required making various private methods protected since NamedTypeUniques
and AppliedUniques contain an inlined implementation of `put`.

This commit also changes the default load factor of a WeakHashSet from
0.75 to 0.5 to match the load factor we use for HashSets, though note that
Scala 2 has always been using 0.75.

For a history of the usage of WeakHashSet in Scala 2 see:
- scala/scala#247
- scala/scala#2605
- scala/scala#2901
xuwei-k pushed a commit to xuwei-k/scala3 that referenced this pull request Jul 6, 2021
This mimics what Scala 2 has been doing for a long time now and serves
the same purpose: it considerably reduces peak memory usage when
compiling some projects, for example previously compiling the Scalatest
tests required a heap of at least 11 GB, but now it fits in about 4 GB.

This required changing the implementation of WeakHashSet to have
overridable `hash` and `isEqual` methods just like HashSet, it also
required making various private methods protected since NamedTypeUniques
and AppliedUniques contain an inlined implementation of `put`.

This commit also changes the default load factor of a WeakHashSet from
0.75 to 0.5 to match the load factor we use for HashSets, though note that
Scala 2 has always been using 0.75.

For a history of the usage of WeakHashSet in Scala 2 see:
- scala/scala#247
- scala/scala#2605
- scala/scala#2901
BarkingBad pushed a commit to BarkingBad/dotty that referenced this pull request Jul 23, 2021
This mimics what Scala 2 has been doing for a long time now and serves
the same purpose: it considerably reduces peak memory usage when
compiling some projects, for example previously compiling the Scalatest
tests required a heap of at least 11 GB, but now it fits in about 4 GB.

This required changing the implementation of WeakHashSet to have
overridable `hash` and `isEqual` methods just like HashSet, it also
required making various private methods protected since NamedTypeUniques
and AppliedUniques contain an inlined implementation of `put`.

This commit also changes the default load factor of a WeakHashSet from
0.75 to 0.5 to match the load factor we use for HashSets, though note that
Scala 2 has always been using 0.75.

For a history of the usage of WeakHashSet in Scala 2 see:
- scala/scala#247
- scala/scala#2605
- scala/scala#2901
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
8 participants