While trying to improve ArrayList performance for
5103956: (coll) Suggested improvement to speed up ArrayList<E> get and set calls
I encountered an anomaly in server compiler optimization.
Basically, since the performance of ArrayList.get() and set() is critical,
we were willing to perform code micro-tweaks to make hotspot optimizers happy.
In particular, for error-handling, we had ArrayList.rangeCheck:
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
}
We refactored this to make the client compiler happier.
All of the possible refactorings gave equal performance under the server compiler,
except one. Here are 3 refactorings:
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
----------
if (index >= size)
throw outOfBoundsException(index);
private IndexOutOfBoundsException outOfBoundsException(int index) {
return new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
}
----------
if (index >= size)
outOfBounds(index);
private void outOfBounds(int index) {
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
}
----------
The refactoring using outOfBounds is dramatically slower on my microbenchmark
than the others. Using j2se/test/java/util/ArrayList/RangeCheckMicroBenchmark.java
I get, e.g. on solaris-amd64:
==> javac -Xlint:all RangeCheckMicroBenchmark.java
==> java -server -esa -ea RangeCheckMicroBenchmark
Method Millis Ratio
get 53 1.000
set 48 0.907
get/set 188 3.487
add/remove at end 1360 25.210
vs.
==> javac -Xlint:all RangeCheckMicroBenchmark.java
==> java -server -esa -ea RangeCheckMicroBenchmark
Method Millis Ratio
get 180 1.000
set 363 2.006
get/set 514 2.844
add/remove at end 1227 6.784
Notice the up-to-factor-of-8 decrease in performance. A big penalty for
just moving some code around.
I'm not a hotspot engineer, but it looks like methods that unconditionally
throw confuse the optimizer. But such methods are typical of attempts to
move error handling into a separate method, and is effective at speeding
up the client compiler, so users are likely to do this sort of thing.
The server compiler needs to examine outOfBounds and understand that it throws,
but not inline it. Maybe.
5103956: (coll) Suggested improvement to speed up ArrayList<E> get and set calls
I encountered an anomaly in server compiler optimization.
Basically, since the performance of ArrayList.get() and set() is critical,
we were willing to perform code micro-tweaks to make hotspot optimizers happy.
In particular, for error-handling, we had ArrayList.rangeCheck:
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
}
We refactored this to make the client compiler happier.
All of the possible refactorings gave equal performance under the server compiler,
except one. Here are 3 refactorings:
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
----------
if (index >= size)
throw outOfBoundsException(index);
private IndexOutOfBoundsException outOfBoundsException(int index) {
return new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
}
----------
if (index >= size)
outOfBounds(index);
private void outOfBounds(int index) {
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
}
----------
The refactoring using outOfBounds is dramatically slower on my microbenchmark
than the others. Using j2se/test/java/util/ArrayList/RangeCheckMicroBenchmark.java
I get, e.g. on solaris-amd64:
==> javac -Xlint:all RangeCheckMicroBenchmark.java
==> java -server -esa -ea RangeCheckMicroBenchmark
Method Millis Ratio
get 53 1.000
set 48 0.907
get/set 188 3.487
add/remove at end 1360 25.210
vs.
==> javac -Xlint:all RangeCheckMicroBenchmark.java
==> java -server -esa -ea RangeCheckMicroBenchmark
Method Millis Ratio
get 180 1.000
set 363 2.006
get/set 514 2.844
add/remove at end 1227 6.784
Notice the up-to-factor-of-8 decrease in performance. A big penalty for
just moving some code around.
I'm not a hotspot engineer, but it looks like methods that unconditionally
throw confuse the optimizer. But such methods are typical of attempts to
move error handling into a separate method, and is effective at speeding
up the client compiler, so users are likely to do this sort of thing.
The server compiler needs to examine outOfBounds and understand that it throws,
but not inline it. Maybe.
- relates to
-
JDK-5103956 (coll) Suggested improvement to speed up ArrayList<E> get and set calls
-
- Closed
-