From f414d72dd5fa15f4c4583a5c081022b0da5be28f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=9F=E4=BB=99=E8=80=81=E4=BA=BA?= Date: Sat, 15 Jun 2019 13:43:36 +0800 Subject: [PATCH] Rewrite AtomicRangeInteger for higher performance (#2874) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Typical test benchmark for performance improvement. Check PR for more detail. /** * # JMH version: 1.21 * # VM version: JDK 1.8.0_111, Java HotSpot(TM) 64-Bit Server VM, 25.111-b14 * # VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/bin/java * # VM options: -Dfile.encoding=UTF-8 * # Warmup: 3 iterations, 10 s each * # Measurement: 5 iterations, 1000 ms each, 1000 calls per op * # Timeout: 10 min per iteration * # Threads: 1024 threads, ***WARNING: Synchronize iterations are disabled!*** * # Benchmark mode: Throughput, ops/time * # Benchmark: org.apache.skywalking.apm.commons.datacarrier.common.AtomicRangeIntegerTest.testNewGetAndIncrementPerformance * * # Run progress: 0.00% complete, ETA 00:01:10 * # Fork: 1 of 1 * # Warmup Iteration 1: 2078993549.108 ops/s * # Warmup Iteration 2: 6408026002.297 ops/s * # Warmup Iteration 3: 5455960135.636 ops/s * Iteration 1: 165330.972 ops/s * Iteration 2: 95420.526 ops/s * Iteration 3: 173340.089 ops/s * Iteration 4: 94214.669 ops/s * Iteration 5: 261606.088 ops/s * * * Result "org.apache.skywalking.apm.commons.datacarrier.common.AtomicRangeIntegerTest.testNewGetAndIncrementPerformance": * 157982.469 ±(99.9%) 265443.465 ops/s [Average] * (min, avg, max) = (94214.669, 157982.469, 261606.088), stdev = 68934.825 * CI (99.9%): [≈ 0, 423425.934] (assumes normal distribution) * * * # JMH version: 1.21 * # VM version: JDK 1.8.0_111, Java HotSpot(TM) 64-Bit Server VM, 25.111-b14 * # VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/bin/java * # VM options: -Dfile.encoding=UTF-8 * # Warmup: 3 iterations, 10 s each * # Measurement: 5 iterations, 1000 ms each, 1000 calls per op * # Timeout: 10 min per iteration * # Threads: 1024 threads, ***WARNING: Synchronize iterations are disabled!*** * # Benchmark mode: Throughput, ops/time * # Benchmark: org.apache.skywalking.apm.commons.datacarrier.common.AtomicRangeIntegerTest.testOriGetAndIncrementPerformance * * # Run progress: 50.00% complete, ETA 00:00:43 * # Fork: 1 of 1 * # Warmup Iteration 1: 3345520433.355 ops/s * # Warmup Iteration 2: 4823749249.799 ops/s * # Warmup Iteration 3: 6676209367.819 ops/s * Iteration 1: 34885.954 ops/s * Iteration 2: 33128.147 ops/s * Iteration 3: 31300.865 ops/s * Iteration 4: 19188.602 ops/s * Iteration 5: 34664.282 ops/s * * * Result "org.apache.skywalking.apm.commons.datacarrier.common.AtomicRangeIntegerTest.testOriGetAndIncrementPerformance": * 30633.570 ±(99.9%) 25249.253 ops/s [Average] * (min, avg, max) = (19188.602, 30633.570, 34885.954), stdev = 6557.151 * CI (99.9%): [5384.317, 55882.822] (assumes normal distribution) * * * # Run complete. Total time: 00:01:26 * * REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on * why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial * experiments, perform baseline and negative tests that provide experimental control, make sure * the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts. * Do not assume the numbers tell you what you want them to tell. * * Benchmark Mode Cnt Score Error Units * AtomicRangeIntegerTest.testNewGetAndIncrementPerformance thrpt 5 157982.469 ± 265443.465 ops/s * AtomicRangeIntegerTest.testOriGetAndIncrementPerformance thrpt 5 30633.570 ± 25249.253 ops/s */ --- .../common/AtomicRangeInteger.java | 12 +- .../common/AtomicRangeIntegerTest.java | 154 ++++++++++++++++++ 2 files changed, 160 insertions(+), 6 deletions(-) diff --git a/apm-commons/apm-datacarrier/src/main/java/org/apache/skywalking/apm/commons/datacarrier/common/AtomicRangeInteger.java b/apm-commons/apm-datacarrier/src/main/java/org/apache/skywalking/apm/commons/datacarrier/common/AtomicRangeInteger.java index 6074303521..ba3667d5f1 100644 --- a/apm-commons/apm-datacarrier/src/main/java/org/apache/skywalking/apm/commons/datacarrier/common/AtomicRangeInteger.java +++ b/apm-commons/apm-datacarrier/src/main/java/org/apache/skywalking/apm/commons/datacarrier/common/AtomicRangeInteger.java @@ -38,15 +38,15 @@ public class AtomicRangeInteger extends Number implements Serializable { } public final int getAndIncrement() { - int current; int next; do { - current = this.value.get(); - next = current >= this.endValue ? this.startValue : current + 1; - } - while (!this.value.compareAndSet(current, next)); + next = this.value.incrementAndGet(); + if (next > endValue && this.value.compareAndSet(next, startValue)) { + return endValue; + } + } while (next > endValue); - return current; + return next - 1; } public final int get() { diff --git a/apm-commons/apm-datacarrier/src/test/java/org/apache/skywalking/apm/commons/datacarrier/common/AtomicRangeIntegerTest.java b/apm-commons/apm-datacarrier/src/test/java/org/apache/skywalking/apm/commons/datacarrier/common/AtomicRangeIntegerTest.java index 30fd2113ad..fb53f31474 100644 --- a/apm-commons/apm-datacarrier/src/test/java/org/apache/skywalking/apm/commons/datacarrier/common/AtomicRangeIntegerTest.java +++ b/apm-commons/apm-datacarrier/src/test/java/org/apache/skywalking/apm/commons/datacarrier/common/AtomicRangeIntegerTest.java @@ -21,11 +21,67 @@ package org.apache.skywalking.apm.commons.datacarrier.common; import org.junit.Assert; import org.junit.Test; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicInteger; /** * Created by xin on 2017/7/14. */ public class AtomicRangeIntegerTest { + + static class AtomicRangeIntegerOri extends Number implements Serializable { + private static final long serialVersionUID = -4099792402691141643L; + private AtomicInteger value; + private int startValue; + private int endValue; + + public AtomicRangeIntegerOri(int startValue, int maxValue) { + this.value = new AtomicInteger(startValue); + this.startValue = startValue; + this.endValue = maxValue - 1; + } + + public final int getAndIncrement() { + int current; + int next; + do { + current = this.value.get(); + next = current >= this.endValue ? this.startValue : current + 1; + } + while (!this.value.compareAndSet(current, next)); + + return current; + } + + public final int get() { + return this.value.get(); + } + + public int intValue() { + return this.value.intValue(); + } + + public long longValue() { + return this.value.longValue(); + } + + public float floatValue() { + return this.value.floatValue(); + } + + public double doubleValue() { + return this.value.doubleValue(); + } + } + + private static AtomicRangeInteger ATOMIC_NEW = new AtomicRangeInteger(0, 100); + private static AtomicRangeIntegerOri ATOMIC_ORI = new AtomicRangeIntegerOri(0, 100); + @Test public void testGetAndIncrement() { AtomicRangeInteger atomicI = new AtomicRangeInteger(0, 10); @@ -39,4 +95,102 @@ public class AtomicRangeIntegerTest { Assert.assertEquals(1, (int)atomicI.floatValue()); Assert.assertEquals(1, (int)atomicI.doubleValue()); } + + @Test + @Benchmark + public void testOriGetAndIncrementPerformance() { + ATOMIC_ORI.getAndIncrement(); + } + + @Test + @Benchmark + public void testNewGetAndIncrementPerformance() { + ATOMIC_NEW.getAndIncrement(); + } + + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(AtomicRangeIntegerTest.class.getSimpleName()) + .forks(1) + .warmupIterations(3) + .threads(128) + .syncIterations(false) + .output("/tmp/jmh.log") + .measurementIterations(5) + .build(); + + new Runner(opt).run(); + } + + /** + * # JMH version: 1.21 + * # VM version: JDK 1.8.0_111, Java HotSpot(TM) 64-Bit Server VM, 25.111-b14 + * # VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/bin/java + * # VM options: -Dfile.encoding=UTF-8 + * # Warmup: 3 iterations, 10 s each + * # Measurement: 5 iterations, 10 s each + * # Timeout: 10 min per iteration + * # Threads: 128 threads, ***WARNING: Synchronize iterations are disabled!*** + * # Benchmark mode: Throughput, ops/time + * # Benchmark: org.apache.skywalking.apm.commons.datacarrier.common.AtomicRangeIntegerTest.testNewGetAndIncrementPerformance + * + * # Run progress: 0.00% complete, ETA 00:02:40 + * # Fork: 1 of 1 + * # Warmup Iteration 1: 41358475.014 ops/s + * # Warmup Iteration 2: 40973232.064 ops/s + * # Warmup Iteration 3: 41310422.853 ops/s + * Iteration 1: 41557782.370 ops/s + * Iteration 2: 42723032.686 ops/s + * Iteration 3: 41957321.407 ops/s + * Iteration 4: 40708422.580 ops/s + * Iteration 5: 40424870.574 ops/s + * + * + * Result "org.apache.skywalking.apm.commons.datacarrier.common.AtomicRangeIntegerTest.testNewGetAndIncrementPerformance": + * 41474285.923 ±(99.9%) 3595500.827 ops/s [Average] + * (min, avg, max) = (40424870.574, 41474285.923, 42723032.686), stdev = 933740.147 + * CI (99.9%): [37878785.096, 45069786.751] (assumes normal distribution) + * + * + * # JMH version: 1.21 + * # VM version: JDK 1.8.0_111, Java HotSpot(TM) 64-Bit Server VM, 25.111-b14 + * # VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/bin/java + * # VM options: -Dfile.encoding=UTF-8 + * # Warmup: 3 iterations, 10 s each + * # Measurement: 5 iterations, 10 s each + * # Timeout: 10 min per iteration + * # Threads: 128 threads, ***WARNING: Synchronize iterations are disabled!*** + * # Benchmark mode: Throughput, ops/time + * # Benchmark: org.apache.skywalking.apm.commons.datacarrier.common.AtomicRangeIntegerTest.testOriGetAndIncrementPerformance + * + * # Run progress: 50.00% complete, ETA 00:01:25 + * # Fork: 1 of 1 + * # Warmup Iteration 1: 14169937.124 ops/s + * # Warmup Iteration 2: 14087015.239 ops/s + * # Warmup Iteration 3: 13955313.979 ops/s + * Iteration 1: 13984516.590 ops/s + * Iteration 2: 13913174.492 ops/s + * Iteration 3: 13824113.805 ops/s + * Iteration 4: 14886990.831 ops/s + * Iteration 5: 14388283.816 ops/s + * + * + * Result "org.apache.skywalking.apm.commons.datacarrier.common.AtomicRangeIntegerTest.testOriGetAndIncrementPerformance": + * 14199415.907 ±(99.9%) 1697559.657 ops/s [Average] + * (min, avg, max) = (13824113.805, 14199415.907, 14886990.831), stdev = 440850.852 + * CI (99.9%): [12501856.250, 15896975.564] (assumes normal distribution) + * + * + * # Run complete. Total time: 00:02:51 + * + * REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on + * why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial + * experiments, perform baseline and negative tests that provide experimental control, make sure + * the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts. + * Do not assume the numbers tell you what you want them to tell. + * + * Benchmark Mode Cnt Score Error Units + * AtomicRangeIntegerTest.testNewGetAndIncrementPerformance thrpt 5 41474285.923 ± 3595500.827 ops/s + * AtomicRangeIntegerTest.testOriGetAndIncrementPerformance thrpt 5 14199415.907 ± 1697559.657 ops/s + */ } -- GitLab