Replace accumulation with blackhole.consume (#9275)

Motivation:

SpotJMHBugs reports that accumulating a value as a way of eliding dead code
elimination may be inadvisable, as discussed in
`JMHSample_34_SafeLooping::measureWrong_2`. Change the test so that it consumes
the response with `Blackhole::consume` instead.

Modifications:

- Replace addition of results with explicit `blackhole.consume()` call

Result:

Tests work as before, but with different benchmark numbers.
This commit is contained in:
Alex Blewitt 2019-06-25 20:46:26 +01:00 committed by Norman Maurer
parent 672fa0c779
commit 52169cba95
4 changed files with 40 additions and 58 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2017 The Netty Project * Copyright 2019 The Netty Project
* *
* The Netty Project licenses this file to you under the Apache License, * The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance * version 2.0 (the "License"); you may not use this file except in compliance
@ -22,6 +22,7 @@ import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -151,68 +152,56 @@ public class HttpMethodMapBenchmark extends AbstractMicrobenchmark {
} }
@Benchmark @Benchmark
public int oldMapKnownMethods() throws Exception { public void oldMapKnownMethods(Blackhole bh) throws Exception {
int x = 0;
for (int i = 0; i < KNOWN_METHODS.length; ++i) { for (int i = 0; i < KNOWN_METHODS.length; ++i) {
x += OLD_MAP.get(KNOWN_METHODS[i]).toString().length(); bh.consume(OLD_MAP.get(KNOWN_METHODS[i]));
} }
return x;
} }
@Benchmark @Benchmark
public int newMapKnownMethods() throws Exception { public void newMapKnownMethods(Blackhole bh) throws Exception {
int x = 0;
for (int i = 0; i < KNOWN_METHODS.length; ++i) { for (int i = 0; i < KNOWN_METHODS.length; ++i) {
x += NEW_MAP.get(KNOWN_METHODS[i]).toString().length(); bh.consume(NEW_MAP.get(KNOWN_METHODS[i]));
} }
return x;
} }
@Benchmark @Benchmark
public int oldMapMixMethods() throws Exception { public void oldMapMixMethods(Blackhole bh) throws Exception {
int x = 0;
for (int i = 0; i < MIXED_METHODS.length; ++i) { for (int i = 0; i < MIXED_METHODS.length; ++i) {
HttpMethod method = OLD_MAP.get(MIXED_METHODS[i]); HttpMethod method = OLD_MAP.get(MIXED_METHODS[i]);
if (method != null) { if (method != null) {
x += method.toString().length(); bh.consume(method);
} }
} }
return x;
} }
@Benchmark @Benchmark
public int newMapMixMethods() throws Exception { public void newMapMixMethods(Blackhole bh) throws Exception {
int x = 0;
for (int i = 0; i < MIXED_METHODS.length; ++i) { for (int i = 0; i < MIXED_METHODS.length; ++i) {
HttpMethod method = NEW_MAP.get(MIXED_METHODS[i]); HttpMethod method = NEW_MAP.get(MIXED_METHODS[i]);
if (method != null) { if (method != null) {
x += method.toString().length(); bh.consume(method);
} }
} }
return x;
} }
@Benchmark @Benchmark
public int oldMapUnknownMethods() throws Exception { public void oldMapUnknownMethods(Blackhole bh) throws Exception {
int x = 0;
for (int i = 0; i < UNKNOWN_METHODS.length; ++i) { for (int i = 0; i < UNKNOWN_METHODS.length; ++i) {
HttpMethod method = OLD_MAP.get(UNKNOWN_METHODS[i]); HttpMethod method = OLD_MAP.get(UNKNOWN_METHODS[i]);
if (method != null) { if (method != null) {
x += method.toString().length(); bh.consume(method);
} }
} }
return x;
} }
@Benchmark @Benchmark
public int newMapUnknownMethods() throws Exception { public void newMapUnknownMethods(Blackhole bh) throws Exception {
int x = 0;
for (int i = 0; i < UNKNOWN_METHODS.length; ++i) { for (int i = 0; i < UNKNOWN_METHODS.length; ++i) {
HttpMethod method = NEW_MAP.get(UNKNOWN_METHODS[i]); HttpMethod method = NEW_MAP.get(UNKNOWN_METHODS[i]);
if (method != null) { if (method != null) {
x += method.toString().length(); bh.consume(method);
} }
} }
return x;
} }
} }

View File

@ -20,6 +20,7 @@ import io.netty.util.concurrent.FastThreadLocal;
import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.infra.Blackhole;
import java.util.Random; import java.util.Random;
@ -56,20 +57,16 @@ public class FastThreadLocalFastPathBenchmark extends AbstractMicrobenchmark {
} }
@Benchmark @Benchmark
public int jdkThreadLocalGet() { public void jdkThreadLocalGet(Blackhole bh) {
int result = 0;
for (ThreadLocal<Integer> i: jdkThreadLocals) { for (ThreadLocal<Integer> i: jdkThreadLocals) {
result += i.get(); bh.consume(i.get());
} }
return result;
} }
@Benchmark @Benchmark
public int fastThreadLocal() { public void fastThreadLocal(Blackhole bh) {
int result = 0;
for (FastThreadLocal<Integer> i: fastThreadLocals) { for (FastThreadLocal<Integer> i: fastThreadLocals) {
result += i.get(); bh.consume(i.get());
} }
return result;
} }
} }

View File

@ -20,6 +20,7 @@ import io.netty.util.concurrent.FastThreadLocal;
import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.infra.Blackhole;
import java.util.Random; import java.util.Random;
@ -60,20 +61,16 @@ public class FastThreadLocalSlowPathBenchmark extends AbstractMicrobenchmark {
} }
@Benchmark @Benchmark
public int jdkThreadLocalGet() { public void jdkThreadLocalGet(Blackhole bh) {
int result = 0;
for (ThreadLocal<Integer> i: jdkThreadLocals) { for (ThreadLocal<Integer> i: jdkThreadLocals) {
result += i.get(); bh.consume(i.get());
} }
return result;
} }
@Benchmark @Benchmark
public int fastThreadLocal() { public void fastThreadLocal(Blackhole bh) {
int result = 0;
for (FastThreadLocal<Integer> i: fastThreadLocals) { for (FastThreadLocal<Integer> i: fastThreadLocals) {
result += i.get(); bh.consume(i.get());
} }
return result;
} }
} }

View File

@ -35,6 +35,7 @@ import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -68,23 +69,23 @@ public class ReadOnlyHttp2HeadersBenchmark extends AbstractMicrobenchmark {
@Benchmark @Benchmark
@BenchmarkMode(Mode.AverageTime) @BenchmarkMode(Mode.AverageTime)
public int defaultTrailers() { public void defaultTrailers(Blackhole bh) {
Http2Headers headers = new DefaultHttp2Headers(false); Http2Headers headers = new DefaultHttp2Headers(false);
for (int i = 0; i < headerCount; ++i) { for (int i = 0; i < headerCount; ++i) {
headers.add(headerNames[i], headerValues[i]); headers.add(headerNames[i], headerValues[i]);
} }
return iterate(headers); iterate(headers, bh);
} }
@Benchmark @Benchmark
@BenchmarkMode(Mode.AverageTime) @BenchmarkMode(Mode.AverageTime)
public int readOnlyTrailers() { public void readOnlyTrailers(Blackhole bh) {
return iterate(ReadOnlyHttp2Headers.trailers(false, buildPairs())); iterate(ReadOnlyHttp2Headers.trailers(false, buildPairs()), bh);
} }
@Benchmark @Benchmark
@BenchmarkMode(Mode.AverageTime) @BenchmarkMode(Mode.AverageTime)
public int defaultClientHeaders() { public void defaultClientHeaders(Blackhole bh) {
Http2Headers headers = new DefaultHttp2Headers(false); Http2Headers headers = new DefaultHttp2Headers(false);
for (int i = 0; i < headerCount; ++i) { for (int i = 0; i < headerCount; ++i) {
headers.add(headerNames[i], headerValues[i]); headers.add(headerNames[i], headerValues[i]);
@ -93,39 +94,37 @@ public class ReadOnlyHttp2HeadersBenchmark extends AbstractMicrobenchmark {
headers.scheme(HttpScheme.HTTPS.name()); headers.scheme(HttpScheme.HTTPS.name());
headers.path(path); headers.path(path);
headers.authority(authority); headers.authority(authority);
return iterate(headers); iterate(headers, bh);
} }
@Benchmark @Benchmark
@BenchmarkMode(Mode.AverageTime) @BenchmarkMode(Mode.AverageTime)
public int readOnlyClientHeaders() { public void readOnlyClientHeaders(Blackhole bh) {
return iterate(ReadOnlyHttp2Headers.clientHeaders(false, HttpMethod.POST.asciiName(), path, iterate(ReadOnlyHttp2Headers.clientHeaders(false, HttpMethod.POST.asciiName(), path,
HttpScheme.HTTPS.name(), authority, buildPairs())); HttpScheme.HTTPS.name(), authority, buildPairs()), bh);
} }
@Benchmark @Benchmark
@BenchmarkMode(Mode.AverageTime) @BenchmarkMode(Mode.AverageTime)
public int defaultServerHeaders() { public void defaultServerHeaders(Blackhole bh) {
Http2Headers headers = new DefaultHttp2Headers(false); Http2Headers headers = new DefaultHttp2Headers(false);
for (int i = 0; i < headerCount; ++i) { for (int i = 0; i < headerCount; ++i) {
headers.add(headerNames[i], headerValues[i]); headers.add(headerNames[i], headerValues[i]);
} }
headers.status(HttpResponseStatus.OK.codeAsText()); headers.status(HttpResponseStatus.OK.codeAsText());
return iterate(headers); iterate(headers, bh);
} }
@Benchmark @Benchmark
@BenchmarkMode(Mode.AverageTime) @BenchmarkMode(Mode.AverageTime)
public int readOnlyServerHeaders() { public void readOnlyServerHeaders(Blackhole bh) {
return iterate(ReadOnlyHttp2Headers.serverHeaders(false, HttpResponseStatus.OK.codeAsText(), buildPairs())); iterate(ReadOnlyHttp2Headers.serverHeaders(false, HttpResponseStatus.OK.codeAsText(), buildPairs()), bh);
} }
private static int iterate(Http2Headers headers) { private static void iterate(Http2Headers headers, Blackhole bh) {
int length = 0;
for (Map.Entry<CharSequence, CharSequence> entry : headers) { for (Map.Entry<CharSequence, CharSequence> entry : headers) {
length += entry.getKey().length() + entry.getValue().length(); bh.consume(entry);
} }
return length;
} }
private AsciiString[] buildPairs() { private AsciiString[] buildPairs() {