RFR: 7903478: JMH: Use different padding names for B1 and B3 classes to avoid "hiding fields with same name" warnings
Vladimir Sitnikov
vsitnikov at openjdk.org
Tue May 30 13:20:27 UTC 2023
On Mon, 29 May 2023 18:49:29 GMT, Aleksey Shipilev <shade at openjdk.org> wrote:
>> see https://bugs.openjdk.org/browse/CODETOOLS-7903478
>>
>>
>> Sample output:
>>
>>
>> public final class CustomTest_bench_jmhTest {
>>
>> byte p000, p001, p002, p003, p004, p005, p006, p007, p008, p009, p010, p011, p012, p013, p014, p015;
>> byte p016, p017, p018, p019, p020, p021, p022, p023, p024, p025, p026, p027, p028, p029, p030, p031;
>> byte p032, p033, p034, p035, p036, p037, p038, p039, p040, p041, p042, p043, p044, p045, p046, p047;
>> byte p048, p049, p050, p051, p052, p053, p054, p055, p056, p057, p058, p059, p060, p061, p062, p063;
>> ..
>> byte p224, p225, p226, p227, p228, p229, p230, p231, p232, p233, p234, p235, p236, p237, p238, p239;
>> byte p240, p241, p242, p243, p244, p245, p246, p247, p248, p249, p250, p251, p252, p253, p254, p255;
>> int startRndMask;
>> BenchmarkParams benchmarkParams;
>>
>>
>>
>> public class CustomTest_jmhType_B1 extends org.openjdk.jmh.it.annsteal.CustomTest {
>> byte b1_000, b1_001, b1_002, b1_003, b1_004, b1_005, b1_006, b1_007, b1_008, b1_009, b1_010, b1_011, b1_012, b1_013, b1_014, b1_015;
>> byte b1_016, b1_017, b1_018, b1_019, b1_020, b1_021, b1_022, b1_023, b1_024, b1_025, b1_026, b1_027, b1_028, b1_029, b1_030, b1_031;
>> ...
>> byte b1_224, b1_225, b1_226, b1_227, b1_228, b1_229, b1_230, b1_231, b1_232, b1_233, b1_234, b1_235, b1_236, b1_237, b1_238, b1_239;
>> byte b1_240, b1_241, b1_242, b1_243, b1_244, b1_245, b1_246, b1_247, b1_248, b1_249, b1_250, b1_251, b1_252, b1_253, b1_254, b1_255;
>> }
>>
>>
>>
>> public class CustomTest_jmhType_B3 extends CustomTest_jmhType_B2 {
>> byte b3_000, b3_001, b3_002, b3_003, b3_004, b3_005, b3_006, b3_007, b3_008, b3_009, b3_010, b3_011, b3_012, b3_013, b3_014, b3_015;
>> byte b3_016, b3_017, b3_018, b3_019, b3_020, b3_021, b3_022, b3_023, b3_024, b3_025, b3_026, b3_027, b3_028, b3_029, b3_030, b3_031;
>> ...
>> byte b3_224, b3_225, b3_226, b3_227, b3_228, b3_229, b3_230, b3_231, b3_232, b3_233, b3_234, b3_235, b3_236, b3_237, b3_238, b3_239;
>> byte b3_240, b3_241, b3_242, b3_243, b3_244, b3_245, b3_246, b3_247, b3_248, b3_249, b3_250, b3_251, b3_252, b3_253, b3_254, b3_255;
>> }
>
> jmh-core/src/main/java/org/openjdk/jmh/generators/core/Paddings.java line 42:
>
>> 40: throw new IllegalArgumentException("prefix must not be empty");
>> 41: }
>> 42: StringJoiner sj = new StringJoiner("");
>
> What's the point of rewriting this to `StringJoiner`? `StringBuilder` optimizations would be nice to have, if available in the JIT.
I doubt `StringBuilder` optimizations are available in JIT since the code uses for-loop and a non-trivial `String.format`. At the same time, `StringJoiner` can estimate the target buffer size before it joins.
`StringBuilder` have to re-allocate the internal array.
Moving `new StringBuilder()` outside of a loop and using `setLength(0)`
@Fork(value = 3, jvmArgsPrepend = {"-Xmx128m"})
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
String prefix = "b1";
@Benchmark
public List<String> paddings_list_old() {
List<String> list = new ArrayList<>();
for (int p = 0; p < 16; p++) {
StringBuilder sb = new StringBuilder();
sb.append(" ");
sb.append(String.format("byte p%03d", p * 16 + 0));
for (int q = 1; q < 16; q++) {
sb.append(String.format(", p%03d", p * 16 + q));
}
sb.append(";");
list.add(sb.toString());
}
return list;
}
@Benchmark
public List<String> paddings_list_reusedBuilder() {
List<String> list = new ArrayList<>();
StringBuilder sb = new StringBuilder();
for (int p = 0; p < 16; p++) {
sb.setLength(0);
sb.append(" ");
sb.append(String.format("byte p%03d", p * 16 + 0));
for (int q = 1; q < 16; q++) {
sb.append(String.format(", p%03d", p * 16 + q));
}
sb.append(";");
list.add(sb.toString());
}
return list;
}
@Benchmark
public String paddings_str_preallocatedBuilder() {
StringBuilder sb = new StringBuilder(1680);
for (int p = 0; p < 16; p++) {
sb.append(" ");
sb.append(String.format("byte p%03d", p * 16 + 0));
for (int q = 1; q < 16; q++) {
sb.append(String.format(", p%03d", p * 16 + q));
}
sb.append(";");
sb.append(System.lineSeparator());
}
return sb.toString();
}
@Benchmark
public String paddings_str_joiner() {
String prefix = this.prefix;
StringJoiner sj = new StringJoiner("");
for (int p = 0; p < 16; p++) {
sj.add(" byte ");
for (int q = 0; q < 16; q++) {
if (q != 0) {
sj.add(", ");
}
sj.add(prefix);
sj.add(String.format("%03d", p * 16 + q));
}
sj.add(";");
sj.add(System.lineSeparator());
}
return sj.toString();
}
yields (M1, Java 17.0.7, JMH 1.36)
Benchmark Mode Cnt Score Error Units
Bench.paddings_list_old avgt 15 106,097 ± 1,265 us/op
Bench.paddings_list_old:·gc.alloc.rate avgt 15 3220,415 ± 38,574 MB/sec
Bench.paddings_list_old:·gc.alloc.rate.norm avgt 15 358280,045 ± 0,002 B/op
Bench.paddings_list_old:·gc.count avgt 15 648,000 counts
Bench.paddings_list_old:·gc.time avgt 15 178,000 ms
Bench.paddings_list_reusedBuilder avgt 15 107,728 ± 6,013 us/op
Bench.paddings_list_reusedBuilder:·gc.alloc.rate avgt 15 3134,032 ± 160,778 MB/sec
Bench.paddings_list_reusedBuilder:·gc.alloc.rate.norm avgt 15 353240,045 ± 0,004 B/op
Bench.paddings_list_reusedBuilder:·gc.count avgt 15 631,000 counts
Bench.paddings_list_reusedBuilder:·gc.time avgt 15 243,000 ms
Bench.paddings_str_joiner avgt 15 101,477 ± 0,787 us/op
Bench.paddings_str_joiner:·gc.alloc.rate avgt 15 3272,061 ± 51,613 MB/sec
Bench.paddings_str_joiner:·gc.alloc.rate.norm avgt 15 348184,043 ± 3205,006 B/op
Bench.paddings_str_joiner:·gc.count avgt 15 660,000 counts
Bench.paddings_str_joiner:·gc.time avgt 15 173,000 ms
Bench.paddings_str_preallocatedBuilder avgt 15 110,889 ± 4,376 us/op
Bench.paddings_str_preallocatedBuilder:·gc.alloc.rate avgt 15 3045,859 ± 120,740 MB/sec
Bench.paddings_str_preallocatedBuilder:·gc.alloc.rate.norm avgt 15 353752,049 ± 0,003 B/op
Bench.paddings_str_preallocatedBuilder:·gc.count avgt 15 615,000 counts
Bench.paddings_str_preallocatedBuilder:·gc.time avgt 15 193,000 ms
It is not a dramatic improvement (well, the thing is computed only once anyway), however, the excellent point is that the result of `StringJoiner`-based implementation is a single `String` that is easier to append to `StringWriter` when generating the benchmark code.
I think `StringJoiner` outperforms a carefully pre-allocated `StringBuilder` because the latter has to build the buffer twice (`StringBuilder` can't share the array while `StringJoiner` has no `char[]` array to begin with).
-------------
PR Review Comment: https://git.openjdk.org/jmh/pull/104#discussion_r1210260307
More information about the jmh-dev
mailing list