Optimize sun.invoke.util.BytecodeDescriptor.unparse

Roger Riggs Roger.Riggs at oracle.com
Thu Aug 20 13:55:07 UTC 2020


Hi Chris,

Inlining and simplifying unparseSig(cl, sb) seems straightforward.

Though I wonder if performs differently than just calling 
t.descriptorString()?

The first action of Class.descriptorString is to check for primitives 
and return the basicTypeString
and if not a primitive it calls Class.descriptorString().

It should be equivalent, without extra checks.

Regards, Roger


On 8/13/20 1:31 PM, Christoph Dreis wrote:
> Hi,
>
> I just stumbled upon sun.invoke.util.BytecodeDescriptor.unparse that seems to unnecessarily create a StringBuilder and checks for the given type to be of Object.class twice in certain scenarios.
>
> When I apply the attached patch below with the following isolated benchmark:
>
> @BenchmarkMode(Mode.AverageTime)
> @OutputTimeUnit(TimeUnit.NANOSECONDS)
> @State(Scope.Thread)
> public class MyBenchmark {
>
> 	@State(Scope.Thread)
> 	public static class BenchmarkState {
> 		private Class<?> test = String.class; // long.class;
> 	}
>
> 	@Benchmark
> 	public String unparseNew(BenchmarkState state) {
> 		return BytecodeDescriptor.unparseNew(state.test);
> 	}
>
> 	@Benchmark
> 	public String unparseOld(BenchmarkState state) {
> 		return BytecodeDescriptor.unparseOld(state.test);
> 	}
> }
>
> I get the following results:
> String.class
> Benchmark                                   Mode  Cnt     Score     Error   Units
> MyBenchmark.unparseNew                      avgt   10    47,207 ±   1,918   ns/op
> MyBenchmark.unparseNew:·gc.alloc.rate.norm  avgt   10   232,011 ±   0,002    B/op
> MyBenchmark.unparseOld                      avgt   10    87,197 ±  22,843   ns/op
> MyBenchmark.unparseOld:·gc.alloc.rate.norm  avgt   10   384,020 ±   0,001    B/op
>                                                                                    
> long.class
> Benchmark                                   Mode  Cnt     Score     Error   Units
> MyBenchmark.unparseNew                      avgt   10     4,996 ±    0,022   ns/op
> MyBenchmark.unparseNew:·gc.alloc.rate.norm  avgt   10    ≈ 10⁻⁶               B/op
> MyBenchmark.unparseOld                      avgt   10    13,303 ±    6,305   ns/op
> MyBenchmark.unparseOld:·gc.alloc.rate.norm  avgt   10    80,003 ±    0,001    B/op
>
> As you can see the new way makes things allocation free for primitives and also improves normal classes.
>
> It seems like a relatively trivial improvement. In case you think this is worthwhile, I would appreciate it if someone could sponsor the change.
>
> Cheers,
> Christoph
>
> ======= PATCH =======
> --- a/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java       Thu Aug 13 09:33:28 2020 -0700
> +++ b/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java       Thu Aug 13 19:27:26 2020 +0200
> @@ -110,9 +110,13 @@
>           } else if (type == int.class) {
>               return "I";
>           }
> -        StringBuilder sb = new StringBuilder();
> -        unparseSig(type, sb);
> -        return sb.toString();
> +        Wrapper basicType = Wrapper.forBasicType(type);
> +        char c = basicType.basicTypeChar();
> +        if (c != 'L') {
> +            return basicType.basicTypeString();
> +        } else {
> +            return type.descriptorString();
> +        }
>       }
>
>



More information about the core-libs-dev mailing list