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