From 7f3ff150ab0acc6bab2170d3b8a9795c0b3c3d63 Mon Sep 17 00:00:00 2001 From: Roshi Date: Sat, 29 Jun 2019 15:18:03 +0800 Subject: [PATCH] One more time for heigher performance (#2930) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit V3(this commit) has 20% performance improvement leverages from no `false-sharing` of CPU cache mechanism. /** * Benchmark Mode Cnt Score Error Units * AtomicRangeIntegerTest.testGetAndIncrementV1Performance thrpt 5 13496720.554 ± 240134.803 ops/s * AtomicRangeIntegerTest.testGetAndIncrementV2Performance thrpt 5 39201251.850 ± 1005866.969 ops/s * AtomicRangeIntegerTest.testGetAndIncrementV3Performance thrpt 5 45832615.061 ± 2987464.163 ops/s */ --- .../common/AtomicRangeInteger.java | 26 +-- .../common/AtomicRangeIntegerTest.java | 164 ++++++++---------- .../common/AtomicRangeIntegerV1.java | 73 ++++++++ .../common/AtomicRangeIntegerV2.java | 72 ++++++++ 4 files changed, 237 insertions(+), 98 deletions(-) create mode 100644 apm-commons/apm-datacarrier/src/test/java/org/apache/skywalking/apm/commons/datacarrier/common/AtomicRangeIntegerV1.java create mode 100644 apm-commons/apm-datacarrier/src/test/java/org/apache/skywalking/apm/commons/datacarrier/common/AtomicRangeIntegerV2.java 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 ba3667d5f..4d31823ab 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 @@ -20,19 +20,23 @@ package org.apache.skywalking.apm.commons.datacarrier.common; import java.io.Serializable; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicIntegerArray; /** - * Created by wusheng on 2016/10/25. + * Created by lkxiaolou */ public class AtomicRangeInteger extends Number implements Serializable { private static final long serialVersionUID = -4099792402691141643L; - private AtomicInteger value; + private AtomicIntegerArray values; + + private static final int VALUE_OFFSET = 15; + private int startValue; private int endValue; public AtomicRangeInteger(int startValue, int maxValue) { - this.value = new AtomicInteger(startValue); + this.values = new AtomicIntegerArray(31); + this.values.set(VALUE_OFFSET, startValue); this.startValue = startValue; this.endValue = maxValue - 1; } @@ -40,8 +44,8 @@ public class AtomicRangeInteger extends Number implements Serializable { public final int getAndIncrement() { int next; do { - next = this.value.incrementAndGet(); - if (next > endValue && this.value.compareAndSet(next, startValue)) { + next = this.values.incrementAndGet(VALUE_OFFSET); + if (next > endValue && this.values.compareAndSet(VALUE_OFFSET, next, startValue)) { return endValue; } } while (next > endValue); @@ -50,22 +54,22 @@ public class AtomicRangeInteger extends Number implements Serializable { } public final int get() { - return this.value.get(); + return this.values.get(VALUE_OFFSET); } public int intValue() { - return this.value.intValue(); + return this.values.get(VALUE_OFFSET); } public long longValue() { - return this.value.longValue(); + return this.values.get(VALUE_OFFSET); } public float floatValue() { - return this.value.floatValue(); + return this.values.get(VALUE_OFFSET); } public double doubleValue() { - return this.value.doubleValue(); + return this.values.get(VALUE_OFFSET); } } 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 fb53f3147..a865af6e6 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 @@ -26,61 +26,15 @@ 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); + private static AtomicRangeInteger ATOMIC_V3 = new AtomicRangeInteger(0, 100); + private static AtomicRangeIntegerV1 ATOMIC_V1 = new AtomicRangeIntegerV1(0, 100); + private static AtomicRangeIntegerV2 ATOMIC_V2 = new AtomicRangeIntegerV2(0, 100); @Test public void testGetAndIncrement() { @@ -98,14 +52,20 @@ public class AtomicRangeIntegerTest { @Test @Benchmark - public void testOriGetAndIncrementPerformance() { - ATOMIC_ORI.getAndIncrement(); + public void testGetAndIncrementV1Performance() { + ATOMIC_V1.getAndIncrement(); } @Test @Benchmark - public void testNewGetAndIncrementPerformance() { - ATOMIC_NEW.getAndIncrement(); + public void testGetAndIncrementV2Performance() { + ATOMIC_V2.getAndIncrement(); + } + + @Test + @Benchmark + public void testGetAndIncrementV3Performance() { + ATOMIC_V3.getAndIncrement(); } public static void main(String[] args) throws RunnerException { @@ -126,62 +86,91 @@ public class AtomicRangeIntegerTest { * # 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 + * # VM options: -XX:-RestrictContended -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 + * # Benchmark: org.apache.skywalking.apm.commons.datacarrier.common.AtomicRangeIntegerTest.testGetAndIncrementV1Performance * - * # Run progress: 0.00% complete, ETA 00:02:40 + * # Run progress: 0.00% complete, ETA 00:04:00 * # 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 + * # Warmup Iteration 1: 14087955.036 ops/s + * # Warmup Iteration 2: 15853193.651 ops/s + * # Warmup Iteration 3: 14242562.576 ops/s + * Iteration 1: 13507077.199 ops/s + * Iteration 2: 13524108.304 ops/s + * Iteration 3: 13428875.424 ops/s + * Iteration 4: 13442334.399 ops/s + * Iteration 5: 13581207.442 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) + * Result "org.apache.skywalking.apm.commons.datacarrier.common.AtomicRangeIntegerTest.testGetAndIncrementV1Performance": + * 13496720.554 ±(99.9%) 240134.803 ops/s [Average] + * (min, avg, max) = (13428875.424, 13496720.554, 13581207.442), stdev = 62362.246 + * CI (99.9%): [13256585.750, 13736855.357] (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 + * # VM options: -XX:-RestrictContended -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 + * # Benchmark: org.apache.skywalking.apm.commons.datacarrier.common.AtomicRangeIntegerTest.testGetAndIncrementV2Performance * - * # Run progress: 50.00% complete, ETA 00:01:25 + * # Run progress: 33.33% complete, ETA 00:02:52 * # 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 + * # Warmup Iteration 1: 38963151.964 ops/s + * # Warmup Iteration 2: 38748023.773 ops/s + * # Warmup Iteration 3: 39049777.582 ops/s + * Iteration 1: 39534928.550 ops/s + * Iteration 2: 39020804.604 ops/s + * Iteration 3: 38991508.452 ops/s + * Iteration 4: 39025237.001 ops/s + * Iteration 5: 39433780.645 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) + * Result "org.apache.skywalking.apm.commons.datacarrier.common.AtomicRangeIntegerTest.testGetAndIncrementV2Performance": + * 39201251.850 ±(99.9%) 1005866.969 ops/s [Average] + * (min, avg, max) = (38991508.452, 39201251.850, 39534928.550), stdev = 261220.458 + * CI (99.9%): [38195384.881, 40207118.820] (assumes normal distribution) * * - * # Run complete. Total time: 00:02:51 + * # 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: -XX:-RestrictContended -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.testGetAndIncrementV3Performance + * + * # Run progress: 66.67% complete, ETA 00:01:25 + * # Fork: 1 of 1 + * # Warmup Iteration 1: 45437159.014 ops/s + * # Warmup Iteration 2: 45253129.637 ops/s + * # Warmup Iteration 3: 45394394.135 ops/s + * Iteration 1: 45434263.958 ops/s + * Iteration 2: 45283522.683 ops/s + * Iteration 3: 47116623.190 ops/s + * Iteration 4: 46012311.703 ops/s + * Iteration 5: 45316353.774 ops/s + * + * + * Result "org.apache.skywalking.apm.commons.datacarrier.common.AtomicRangeIntegerTest.testGetAndIncrementV3Performance": + * 45832615.061 ±(99.9%) 2987464.163 ops/s [Average] + * (min, avg, max) = (45283522.683, 45832615.061, 47116623.190), stdev = 775834.956 + * CI (99.9%): [42845150.898, 48820079.225] (assumes normal distribution) + * + * + * # Run complete. Total time: 00:04:17 * * 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 @@ -189,8 +178,9 @@ public class AtomicRangeIntegerTest { * 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 + * Benchmark Mode Cnt Score Error Units + * AtomicRangeIntegerTest.testGetAndIncrementV1Performance thrpt 5 13496720.554 ± 240134.803 ops/s + * AtomicRangeIntegerTest.testGetAndIncrementV2Performance thrpt 5 39201251.850 ± 1005866.969 ops/s + * AtomicRangeIntegerTest.testGetAndIncrementV3Performance thrpt 5 45832615.061 ± 2987464.163 ops/s */ } diff --git a/apm-commons/apm-datacarrier/src/test/java/org/apache/skywalking/apm/commons/datacarrier/common/AtomicRangeIntegerV1.java b/apm-commons/apm-datacarrier/src/test/java/org/apache/skywalking/apm/commons/datacarrier/common/AtomicRangeIntegerV1.java new file mode 100644 index 000000000..4ce28786e --- /dev/null +++ b/apm-commons/apm-datacarrier/src/test/java/org/apache/skywalking/apm/commons/datacarrier/common/AtomicRangeIntegerV1.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +package org.apache.skywalking.apm.commons.datacarrier.common; + +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Created by wusheng on 2016/10/25. + * This is moved from SkyWalking 6.1 + */ +public class AtomicRangeIntegerV1 extends Number implements Serializable { + + private static final long serialVersionUID = -4099792402691141643L; + private AtomicInteger value; + private int startValue; + private int endValue; + + public AtomicRangeIntegerV1(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(); + } +} diff --git a/apm-commons/apm-datacarrier/src/test/java/org/apache/skywalking/apm/commons/datacarrier/common/AtomicRangeIntegerV2.java b/apm-commons/apm-datacarrier/src/test/java/org/apache/skywalking/apm/commons/datacarrier/common/AtomicRangeIntegerV2.java new file mode 100644 index 000000000..15bb9c149 --- /dev/null +++ b/apm-commons/apm-datacarrier/src/test/java/org/apache/skywalking/apm/commons/datacarrier/common/AtomicRangeIntegerV2.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +package org.apache.skywalking.apm.commons.datacarrier.common; + +import java.io.Serializable; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Created by lkxiaolou + * This comes from PR#2874 + */ +public class AtomicRangeIntegerV2 extends Number implements Serializable { + private static final long serialVersionUID = -4099792402691141643L; + private AtomicInteger value; + private int startValue; + private int endValue; + + public AtomicRangeIntegerV2(int startValue, int maxValue) { + this.value = new AtomicInteger(startValue); + this.startValue = startValue; + this.endValue = maxValue - 1; + } + + public final int getAndIncrement() { + int next; + do { + next = this.value.incrementAndGet(); + if (next > endValue && this.value.compareAndSet(next, startValue)) { + return endValue; + } + } while (next > endValue); + + return next - 1; + } + + 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(); + } +}