Remove redundant calls of toString()

Remi Forax forax at univ-mlv.fr
Wed Apr 30 11:08:46 UTC 2014


On 04/29/2014 11:20 AM, Claes Redestad wrote:
>
> On 2014-04-29 09:31, Remi Forax wrote:
>> On 04/28/2014 05:43 PM, Claes Redestad wrote:
>>> On 04/28/2014 08:57 AM, David Holmes wrote:
>>>> On 28/04/2014 1:05 PM, Otávio Gonçalves de Santana wrote:
>>>>> In my opinion not, because Objects.requireNonNull is more readable 
>>>>> than
>>>>> just string.toString. This way is more understandable which field is
>>>>> required and doesn't impact on performance.
>>>>
>>>> An invocation of requireNonNull is potentially more expensive than 
>>>> the implicit null check that happens with foo.toString().
>>>>
>>>> David
>>>> ----- 
>>>
>>> My thought was that these two would be inlined to the exact same 
>>> thing, so I did a quick test to see what happens when you do 
>>> foo.toString() versus Objects.requireNonNull(foo) on a set of 
>>> randomly generated String[]'s with different amounts of null 
>>> elements(0p: no null entries, 1p: 1% null entries etc):
>>>
>>> Benchmark                                     Mode Samples Mean   
>>> Mean error    Units
>>> s.m.ThrowAwayBenchmark.nullToString0p        thrpt 6 356653.044     
>>> 3573.707   ops/ms
>>> s.m.ThrowAwayBenchmark.nullToString1p thrpt         6 353128.903     
>>> 2764.102   ops/ms
>>> s.m.ThrowAwayBenchmark.nullToString10p thrpt         6 
>>> 297956.571     9580.251   ops/ms
>>> s.m.ThrowAwayBenchmark.nullToString50p thrpt         6 
>>> 158172.036     1893.096   ops/ms
>>> s.m.ThrowAwayBenchmark.nullToString100p thrpt         6 
>>> 18194.614      472.091   ops/ms
>>> s.m.ThrowAwayBenchmark.requireNonNull0p thrpt         6 
>>> 357855.126     2979.090   ops/ms
>>> s.m.ThrowAwayBenchmark.requireNonNull1p thrpt         6 
>>> 67601.134     7004.689   ops/ms
>>> s.m.ThrowAwayBenchmark.requireNonNull10p thrpt         6 
>>> 8150.595      538.970   ops/ms
>>> s.m.ThrowAwayBenchmark.requireNonNull50p thrpt         6 
>>> 1604.919      220.903   ops/ms
>>> s.m.ThrowAwayBenchmark.requireNonNull100p thrpt         6 
>>> 820.626       60.752   ops/ms
>>>
>>> Yikes! As long as the value is never null they're inlined nicely and 
>>> neither have the upper hand performance-wise, but as soon as you get 
>>> some null values, Objects.requireNonNull degenerates much faster 
>>> than its foo.toString counterpart. I think this is a JIT issue - 
>>> optimizing exception-paths might not be the highest priority, but 
>>> Objects.requireNonNull is used pretty extensively in the JDK and my 
>>> expectation would be that it shouldn't degrade performance when 
>>> things actually are null now and then.
>>>
>>> /Claes
>>
>> This is a know issue, I think it's not related to the way the JIT 
>> handle exception path but what is called 'profile pollution'.
>> Hotspot JITs have two ways to do a null check, either do nothing (yes 
>> nothing) and let the system do a fault and come back from dead using 
>> a signal handler, this solution is named implicit null check or by 
>> doing an explicit null check, i.e a conditional jump.
>> Implicit null check is faster but if the receiver is null, it cost 
>> you an harm, so the VM profiles receiver to remember if the receiver 
>> of each call can be null or not. The problem is that when you call 
>> foo.toString(), the profile information is associated with the 
>> instruction that does foo.toString() while if the call is 
>> Objects.requireNonNull(foo), the profile associated with the 
>> nullcheck is stored inside the method requireNonNull, so if one call 
>> to requireNonNull in the entire program throw a NPE, the profile 
>> inside requireNonNull now register that it may fail, so for the VM 
>> all calls to requireNonNull may fail. So currently you should not use 
>> requireNonNull is the value is not required to be null*
> I guess I should have given more details. :-) I ran my micros on a 
> number of forked VMs to ensure I don't get excessive run-to-run 
> variations, which among other things avoids profile pollution. Also I 
> sloppily collected very few samples and only did minimal warmup after 
> I saw that the micros produced scores that were orders of magnitude 
> apart and the values stabilized after less than two seconds: Flags: -f 
> 3 -wi 4 -i 2
>
> When I run the micros sequentially in the same VM I see some added 
> degradation for the exceptional cases, but the 0p cases still inline 
> to the optimal(?) 360k ops/ms, so while it seems profile pollution 
> might play in when provoked, HotSpot seems to manage these simple 
> micros well enough.

Ok, so you have to  take a look to the generated assembly code :)
https://wiki.openjdk.java.net/display/HotSpot/PrintAssembly

>> So the problem is that Hotspot blindly trusts the recorded profile 
>> compared to the profile that can come from the arguments of a call.
>> The good news is that recently some patches were included in the jdk9 
>> tree to fix or at least mitigate that issue.
>>
> I've now run tests on JDK8 FCS and a recent internal build of JDK9 and 
> see the same characteristics. How recently are we talking here? :-)

I don't know, I have seen the patches but not dive into the source yet, 
it's on my todo list, maybe it's not even activated by default.

>
>> cheers,
>> Rémi
>> * read that last sentence again, it seems very logical, no ?
>>
> While your reading of the method name and suggested approach makes 
> sense right now, the javadoc for Objects.requireNonNull states "this 
> method is designed primarily for doing parameter validation in methods 
> and constructors". To me, "parameter validation" suggests that we're 
> dealing with unknowns and that there's at least some calculated risk 
> that the input might be null, so the intent is really to encourage 
> explicit fail-fast designs, no?

The great Java Book is clear on that, you shall never try to recover 
from an exception that inherits from RuntimeException :)
A runtime exception is an exception that developers throw at the head of 
their fellow developers to indicate that they should read the fucking 
manual.

> I thus think it makes sense to try and ensure these methods have the 
> same performance characteristics as when provoking an implicit null 
> check by dereferencing an object.

as I said, I think it's time to take a look to the generated assembly code.

>
> /Claes

regards,
Rémi




More information about the core-libs-dev mailing list