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

Optimize Infos for caching in InfoLoader #4981

Merged
merged 3 commits into from
May 9, 2024
Merged

Conversation

gzm0
Copy link
Contributor

@gzm0 gzm0 commented May 2, 2024

This allows us to use the previously calculated infos themselves as
the cached values, reducing memory consumption.

Further, we reduce memory consumption by using immutable maps: Because
a lot of the maps are empty, we do not spend unecessary memory on
empty mutable maps.

This reduces the retained size on the test suite for the infos as
follows:

Component Before [MB] After [MB]
BaseLinker 20 15
Refiner 17 13

As a nice side-effect, this allows us to simplify the InfoLoader. The
simplification mainly stems from the insight, that we do not need
active cache used tracking; control flow is sufficient.

Execution times are unaffected, in fact, the incremental case might
even be a bit faster (non-significant though).

It was a useful abstraction when we were using Traversers to build
entire class infos. However, at this point it's mostly boilerplate.

Changeing this, will help simplify upcoming changes.
@gzm0
Copy link
Contributor Author

gzm0 commented May 2, 2024

@gzm0 gzm0 requested a review from sjrd May 2, 2024 15:44
@gzm0 gzm0 force-pushed the less-mutable branch 2 times, most recently from 63444a1 to adac530 Compare May 4, 2024 06:04
Copy link
Member

@sjrd sjrd left a comment

Choose a reason for hiding this comment

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

Two nits and one more important comment. Looks good overall.

@@ -1517,19 +1516,17 @@ private class AnalyzerRun(config: CommonPhaseConfig, initial: Boolean,
}

private def makeSyntheticMethodInfo(
methodName: MethodName,
namespace: MemberNamespace,
methodsCalled: List[(ClassName, MethodName)] = Nil,
methodsCalledStatically: List[(ClassName, NamespacedMethodName)] = Nil,
instantiatedClasses: List[ClassName] = Nil
Copy link
Member

Choose a reason for hiding this comment

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

Reading this, I realize instantiatedClasses is dead code. Take the opportunity to remove it?

Comment on lines 122 to 126
prevJSCtorInfo = classDef.jsConstructor.map { ctor =>
prevJSCtorInfo
.filter(_.version.sameVersion(ctor.version))
.getOrElse(Infos.generateJSConstructorInfo(ctor))
}
Copy link
Member

Choose a reason for hiding this comment

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

Nit: extract into genJSConstructorInfo(classDef.jsConstructor) to be consistent with the other two?

val jsMethodProps = {
classDef.jsConstructor.map(jsConstructorInfoCache.getInfo(_)).toList :::
exportedMembersInfoCaches.getInfos(classDef.jsMethodProps)
prevMethodInfos = genMethodInfos(classDef.methods)
Copy link
Member

Choose a reason for hiding this comment

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

It's a bit confusing that genMethodInfos uses the previous state of prevMethodInfos right before we reassign it. It doesn't help that there are two such methods that each access state that happens to be reassigned after them, but not the other state. It's not a big deal but it is a big hard to wrap my head around why it's safe.

It seems prevMethodInfos is in fact the only piece of state used by genMethodInfos. Did you consider explicitly passing prevMethodInfos as an argument to genMethodInfos and put the latter in a scope where it cannot access any state?

Concretely, amend this line to

Suggested change
prevMethodInfos = genMethodInfos(classDef.methods)
prevMethodInfos = genMethodInfos(classDef.methods, prevMethodInfos)

and move genMethodInfos outside of private class ClassInfoCache.

The same applies to genJSMethodPropDefInfos and possibly genJSConstructorInfo.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, I agree. I didn't like this as well when I was writing it. Just didn't really follow through on the feeling.

I changed it.

@gzm0 gzm0 force-pushed the less-mutable branch 2 times, most recently from a0ebe2b to b3549f8 Compare May 8, 2024 20:21
@gzm0 gzm0 requested a review from sjrd May 8, 2024 20:23
gzm0 added 2 commits May 9, 2024 08:05
This allows us to use the previously calculated infos themselves as
the cached values, reducing memory consumption.

Further, we reduce memory consumption by using immutable maps: Because
a lot of the maps are empty, we do not spend unecessary memory on
empty mutable maps.

This reduces the retained size on the test suite for the infos as
follows:

| Component  | Before [MB] | After [MB] |
|------------|------------:|-----------:|
| BaseLinker |          20 |         15 |
| Refiner    |          17 |         13 |

As a nice side-effect, this allows us to simplify the InfoLoader. The
simplification mainly stems from the insight, that we do not need
active cache used tracking; control flow is sufficient.

Execution times are unaffected, in fact, the incremental case might
even be a bit faster (non-significant though).
@sjrd sjrd merged commit 24a7f0c into scala-js:main May 9, 2024
3 checks passed
@gzm0 gzm0 deleted the less-mutable branch May 9, 2024 17:06
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.

None yet

2 participants