Covariant overrides on the Buffer Hierachy

Peter Levart peter.levart at gmail.com
Tue Apr 22 09:17:54 UTC 2014


On 04/22/2014 12:02 AM, David M. Lloyd wrote:
> Um, do we *know* that there is a performance cost to covariantly 
> overriding these methods?  There would seem to be enough information 
> to keep them monomorphic, if the optimizer is smart enough to inline 
> the bridge methods and the delegating override method.  The overridden 
> methods in addition can be final, meaning that in the 99% case that 
> you're invoking directly on the desired buffer type, it should be just 
> as optimizable for the same reason that the original methods were 
> optimizable.  The only potentially "slow" invocation path is if you 
> call the method on a Buffer reference, and even then it seems like 
> there's enough information to avoid slowness - and if not, then that 
> seems like a HotSpot problem that is very solvable ("if all overrides 
> of this method call super.xxx(), inline & eliminate them").

It's more complicated than that. Maybe we need an expert for hotspot JIT 
to answer this question, but as the code is written in the Rickard's 
webrev, then the reasoning behind the JIT to keep the monomorphic 
dispatch would have to be more involving. Richard is doing the following 
(in ByteBuffer):

@Override
public ByteBuffer position(int newPosition) {
     super.position(newPosition);
     return this;
}

javac compiles each of the covariant overrides as two methods - one that 
actually "overrides" the virtual method in superclass (has the same 
signature) and calls the covariant-returning method with a virtual 
dispatch. So ByteBuffer.position(int) is compiled as:

public ByteBuffer position(int newPosition) {
     super.position(newPosition);
     return this;
}

public Buffer position(int newPosition) {
     // this is an invokevirtual for position:(I)Ljava/nio/ByteBuffer;
     return (ByteBuffer) position( (int) newPosition);
}


So for a call-site invoking virtual position:(I)Ljava/nio/Buffer; to 
behave the same as if the Buffer.position(int) was final, JIT would have 
to prove a lot of things. It would have to prove that all the methods 
involved in currently loaded hierarchy are basically the same. Among the 
things it would have to prove that, for example, 
ByteBuffer.position(int) returns what Buffer.position(int) returns. I 
don't know if it would help much, but the following covariant override 
might give a better hint to JIT in that respect:

@Override
public ByteBuffer position(int newPosition) {
     return (ByteBuffer) super.position(newPosition);
}

...or it might not, since JIT would have to prove, that the cast is 
actually a no-op (that it always succeeds), which could be done by 
proving that Buffer.position(int) always returns "this", which is the 
same thing it would have to prove with Richard's variant of override.

>
> Is there a real, solid hypothesis that would demonstrate that any of 
> this is not true?

We have to test the performance impact of this change.

Regards, Peter

>
> -- 
> - DML

If you're invoking the covariant-return method in the subtype (which is 
in addition final), then there's no problem, since this method is 
calling super with invokespecial, which I think is



More information about the core-libs-dev mailing list