Allocation of array copy can be eliminated in particular cases
Сергей Цыпанов
sergei.tsypanov at yandex.ru
Mon Nov 18 22:44:00 UTC 2019
Hello,
this proposal was born as a result of discussion in core-libs-dev [1] and IDEA-226474 [2].
Originally I suggested to replace
int count = method.getParameterTypes().length;
with
int count = method.getParameterCount();
Then it turned out that cloned array could be a subject of allocation elimination as any property dereferenced from the copy can be dereferenced from original array:
Consider this test:
@Test
void arrayClone() {
final Object[] objects = new Object[3];
objects[0] = "azaza";
objects[1] = 365;
objects[2] = 9876L;
final Object[] clone = objects.clone();
assertEquals(objects.length, clone.length);
assertSame(objects[0], clone[0]);
assertSame(objects[1], clone[1]);
assertSame(objects[2], clone[2]);
}
Optimizing compiler could drop allocation of 'clone' variable and substitute its usage with original array, which is not done currently:
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class MethodParamCountBenchmark {
private Method method;
@Setup
public void setup() throws Exception { method = getClass().getMethod("toString"); }
@Benchmark
public int getParameterCount() { return method.getParameterCount(); }
@Benchmark
public int getParameterTypes() { return method.getParameterTypes().length; }
}
on my i7-7700 with JDK 11 this benchmark yields these results:
Benchmark Mode Cnt Score Error Units
MethodToStringBenchmark.getParameterCount avgt 25 2,528 ± 0,085 ns/op
MethodToStringBenchmark.getParameterCount:·gc.alloc.rate avgt 25 ≈ 10⁻⁴ MB/sec
MethodToStringBenchmark.getParameterCount:·gc.alloc.rate.norm avgt 25 ≈ 10⁻⁷ B/op
MethodToStringBenchmark.getParameterCount:·gc.count avgt 25 ≈ 0 counts
MethodToStringBenchmark.getParameterTypes avgt 25 7,299 ± 0,410 ns/op
MethodToStringBenchmark.getParameterTypes:·gc.alloc.rate avgt 25 1999,454 ± 89,929 MB/sec
MethodToStringBenchmark.getParameterTypes:·gc.alloc.rate.norm avgt 25 16,000 ± 0,001 B/op
MethodToStringBenchmark.getParameterTypes:·gc.churn.G1_Eden_Space avgt 25 2003,360 ± 91,537 MB/sec
MethodToStringBenchmark.getParameterTypes:·gc.churn.G1_Eden_Space.norm avgt 25 16,030 ± 0,045 B/op
MethodToStringBenchmark.getParameterTypes:·gc.churn.G1_Old_Gen avgt 25 0,004 ± 0,001 MB/sec
MethodToStringBenchmark.getParameterTypes:·gc.churn.G1_Old_Gen.norm avgt 25 ≈ 10⁻⁵ B/op
MethodToStringBenchmark.getParameterTypes:·gc.count avgt 25 2380,000 counts
MethodToStringBenchmark.getParameterTypes:·gc.time avgt 25 1325,000 ms
I.e. intermediate array is allocated even if it doesn't escape the method it is created in.
Is my speculation correct and does it make sence to implement optimization that turns sequence
array -> array.clone() - > clone.length
into
array -> array.length
for the cases clone's visibility scope is predictable?
1) http://mail.openjdk.java.net/pipermail/core-libs-dev/2019-November/063344.html
2) https://youtrack.jetbrains.com/issue/IDEA-226474
Regards,
Sergey Tsypanov
More information about the hotspot-compiler-dev
mailing list