8000 Implement ListBuffer.isEmpty / nonEmpty / head efficiently by jrudolph · Pull Request #5802 · scala/scala · GitHub
[go: up one dir, main page]

Skip to content

Implement ListBuffer.isEmpty / nonEmpty / head efficiently #5802

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

Merged
merged 1 commit into from
Mar 30, 2017

Conversation

jrudolph
Copy link
Contributor

Uses the extra length information in comparison to list to provide efficient implementations.

Evaluating these three methods turns up with about 6-7% of akka-http message parsing.

From the profiles it looks the likely reason are polymorphic invokeinterface calls against List (i.e. just forwarding to start.isEmpty etc wouldn't help).

@scala-jenkins scala-jenkins added this to the 2.12.2 milestone Mar 23, 2017
Copy link
Member
@lrytz lrytz left a comment

Choose a reason for hiding this comment

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

Can you elaborate a bit on the kind of benchmarks you performed?

// Override with efficient implementations using the extra size information available to ListBuffer.
override def isEmpty: Boolean = len == 0
override def nonEmpty: Boolean = len > 0
override def head: A = if (len > 0) start.asInstanceOf[::[A]].head else Nil.head
Copy link
Member

Choose a reason for hiding this comment

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

This one doesn't look very cute.. start.head would be a virtual call with two cases, so C2 should be able to inline it. But before C2 it's probably slower than the monomorphic calls.

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, you are probably right about head. I removed that one.

Uses the extra length information to provide more
efficient implementations.

Evaluating these methods turns up with about 5-6% of akka-http
message parsing.
@jrudolph jrudolph force-pushed the jr/w/ListBuffer-isEmpty-nonEmpty branch from 43f194d to 40f0514 Compare March 27, 2017 10:16
@jrudolph
Copy link
Contributor Author

I basically just saw these turning up in play / akka-http benchmarks.

It seems one reason that I saw head turning up at all was that it hit the MaxInlineDepth at the wrong place, so that the head call wouldn't benefit from the bimorphic invocation optimization in C2 you hinted at above. TraversableForwarder adds a few frames which makes it more likely that a call against List (which should be always bimorphic) ends up going through a polymorphic call-site when not all forwarder frames cannot be inlined. The code I added for head is very similar to what C2 would optimize to, but I agree that it is not clear if optimizing at such a level makes sense.

For the isEmpty / nonEmpty case, it seems obvious that a single integer comparison would be faster than calling through to List to figure it out.

@lrytz
Copy link
Member
lrytz commented Mar 27, 2017

The default MaxInlineLevel is possibly low for Scala. I think @retronym experimented with a patch to the JDK so that small methods (35 bytes/MaxInlineSize) are not counted. Anyway, i think the current patch makes sense. cc @Ichoran

@jvican
Copy link
Member
jvican commented Mar 27, 2017

@lrytz @retronym Is that JDK patch available? I'd like to have a look and experiment with it if it's open-source.

@lrytz
Copy link
Member
lrytz commented Mar 27, 2017

I searched a bit before but couldn't find it

@dwijnand
Copy link
Member

Was it this one? It's a JDK patch by Jason, but it's about forwarders not MaxInlineSize.

https://gist.github.com/retronym/d27090a68570485c3e329a52db0c7b25

@jrudolph
Copy link
Contributor Author

Interesting information.

FWIW, in this case, methods from TraversableForwarder might not qualify as a forwarder in the sense of the patch as they use e.g. underlying.nonEmpty where underlying is a method call itself.

@retronym
Copy link
Member

My JDK patch was intended to make the extra indirection of the trait encoding "invisible" to the inliner heuristics. I've been meaning to get back to it, but I haven't been able to get my dev environment setup for OpenJDK builds recently :(

@retronym
Copy link
Member
retronym commented Mar 28, 2017

I'm seeing a 6% improvement to the performance of our compiler benchmark suite with -XX:MaxInlineLevel=18 (the default is 9). I haven't tried fine tuning of the value.

Related discussion on SO: Why does the JVM has a max inline depth?

It will be interesting to see how much of that improvement I could get under the default limit with my JDK patch applied.

@adriaanm adriaanm mentioned this pull request Mar 28, 2017
7 tasks
@Ichoran
Copy link
Contributor
Ichoran commented Mar 28, 2017

@lrytz - Yes, this patch looks sensible to me too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
64C3
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants
0