Avoid allocations in Executable.getAllGenericParameterTypes
Claes Redestad
claes.redestad at oracle.com
Sat May 23 17:41:08 UTC 2020
Hi Christoph,
I agree this might not be called super often, but the refactoring is
straightforward and I think it clarifies the intent of the code, so
seems like a reasonable enhancement to make.
I've already volunteered to sponsor another patch of yours, so I might
as well pick this up too and test them together.
/Claes
On 2020-05-23 09:53, Christoph Dreis wrote:
> Hi,
>
> I was looking through the code and found a relatively straight-forward optimization in Executable.getAllGenericParameterTypes (attached below).
> The idea is to simply move some code around to avoid unnecessary allocations from getParameters() or array initializations when working with generics and no "real" parameter data is available.
>
> I used the following benchmark:
>
> @BenchmarkMode(Mode.AverageTime)
> @OutputTimeUnit(TimeUnit.NANOSECONDS)
> public class MyBenchmark {
>
> @State(Scope.Benchmark)
> public static class ThreadState {
>
> private Parameter parameter;
>
> public ThreadState() {
> try {
> this.parameter = Test1.class.getDeclaredConstructor(Set.class).getParameters()[0];
> } catch (NoSuchMethodException e) {
> // Do nothing
> }
> }
>
> }
>
> @Benchmark
> public AnnotatedType test(ThreadState threadState) {
> return threadState.parameter.getAnnotatedType();
> }
>
> }
>
> public class Test1 {
> public Test1(Set<?> names) {}
> }
>
> With the attached patch and the benchmark above I get the following results.
>
> Old
> Benchmark Mode Cnt Score Error Units
> MyBenchmark.test avgt 10 358,103 ± 58,098 ns/op
> MyBenchmark.test:·gc.alloc.rate.norm avgt 10 248,021 ± 0,003 B/op
>
> Patched
> MyBenchmark.test avgt 10 309,133 ± 6,319 ns/op
> MyBenchmark.test:·gc.alloc.rate.norm avgt 10 200,016 ± 0,001 B/op
>
>
> I don't think the particular method is called super often, but it seems to be used in ByteBuddy & JUnit as far as I can see.
> In case you think this is worthwhile, I would appreciate it if someone can sponsor this patch.
>
> Cheers,
> Christoph
>
> ========= PATCH ==========
> --- a/src/java.base/share/classes/java/lang/reflect/Executable.java Wed May 13 16:18:16 2020 +0200
> +++ b/src/java.base/share/classes/java/lang/reflect/Executable.java Sat May 23 08:31:23 2020 +0200
> @@ -307,12 +307,12 @@
> final boolean realParamData = hasRealParameterData();
> final Type[] genericParamTypes = getGenericParameterTypes();
> final Type[] nonGenericParamTypes = getParameterTypes();
> - final Type[] out = new Type[nonGenericParamTypes.length];
> - final Parameter[] params = getParameters();
> - int fromidx = 0;
> // If we have real parameter data, then we use the
> // synthetic and mandate flags to our advantage.
> if (realParamData) {
> + final Type[] out = new Type[nonGenericParamTypes.length];
> + final Parameter[] params = getParameters();
> + int fromidx = 0;
> for (int i = 0; i < out.length; i++) {
> final Parameter param = params[i];
> if (param.isSynthetic() || param.isImplicit()) {
> @@ -325,6 +325,7 @@
> fromidx++;
> }
> }
> + return out;
> } else {
> // Otherwise, use the non-generic parameter data.
> // Without method parameter reflection data, we have
> @@ -334,7 +335,6 @@
> return genericParamTypes.length == nonGenericParamTypes.length ?
> genericParamTypes : nonGenericParamTypes;
> }
> - return out;
> }
>
>
More information about the core-libs-dev
mailing list