From 08312eae24bb7e4b472a2c2d3dcd1932676b0f7c Mon Sep 17 00:00:00 2001 From: bstorozhuk Date: Fri, 2 Dec 2016 17:46:49 +0200 Subject: [PATCH] Issue #12 state calculation benchmark shows that most time is spending in System.nanoTime --- .../circuitbreaker/RateLimiterBenchmark.java | 117 ++++++++++++------ .../circuitbreaker/casWithBackOff.txt | 6 - .../stateCalculationBenchmark.txt | 5 + .../internal/AtomicRateLimiter.java | 2 +- 4 files changed, 84 insertions(+), 46 deletions(-) delete mode 100644 src/jmh/java/javaslang/circuitbreaker/casWithBackOff.txt create mode 100644 src/jmh/java/javaslang/circuitbreaker/stateCalculationBenchmark.txt diff --git a/src/jmh/java/javaslang/circuitbreaker/RateLimiterBenchmark.java b/src/jmh/java/javaslang/circuitbreaker/RateLimiterBenchmark.java index 265ff125c3..3029140d48 100644 --- a/src/jmh/java/javaslang/circuitbreaker/RateLimiterBenchmark.java +++ b/src/jmh/java/javaslang/circuitbreaker/RateLimiterBenchmark.java @@ -19,12 +19,11 @@ import java.time.Duration; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.LockSupport; import java.util.function.Supplier; @State(Scope.Benchmark) @OutputTimeUnit(TimeUnit.MICROSECONDS) -@BenchmarkMode(Mode.Throughput) +@BenchmarkMode(Mode.AverageTime) public class RateLimiterBenchmark { public static final int FORK_COUNT = 2; @@ -64,10 +63,9 @@ public void setUp() { @Warmup(iterations = WARMUP_COUNT) @Fork(value = FORK_COUNT) @Measurement(iterations = ITERATION_COUNT) - public void mutex(Blackhole bh) { - synchronized (mutex) { - state = atomicRateLimiter.calculateNextState(Duration.ZERO.toNanos(), state); - } + public void calculateNextState(Blackhole bh) { + AtomicRateLimiter.State next = atomicRateLimiter.calculateNextState(Duration.ZERO.toNanos(), this.state); + bh.consume(next); } @Benchmark @@ -75,10 +73,9 @@ public void mutex(Blackhole bh) { @Warmup(iterations = WARMUP_COUNT) @Fork(value = FORK_COUNT) @Measurement(iterations = ITERATION_COUNT) - public void atomic(Blackhole bh) { - atomicRateLimiter.state.updateAndGet(state -> { - return atomicRateLimiter.calculateNextState(Duration.ZERO.toNanos(), state); - }); + public void nanosToWaitForPermission(Blackhole bh) { + long next = atomicRateLimiter.nanosToWaitForPermission(1, 315L, 31L); + bh.consume(next); } @Benchmark @@ -86,25 +83,9 @@ public void atomic(Blackhole bh) { @Warmup(iterations = WARMUP_COUNT) @Fork(value = FORK_COUNT) @Measurement(iterations = ITERATION_COUNT) - public void atomicBackOf(Blackhole bh) { - AtomicRateLimiter.State prev; - AtomicRateLimiter.State next; - do { - prev = atomicRateLimiter.state.get(); - next = atomicRateLimiter.calculateNextState(Duration.ZERO.toNanos(), prev); - } while (!compareAndSet(prev, next)); - } - - /* - https://arxiv.org/abs/1305.5800 https://dzone.com/articles/wanna-get-faster-wait-bit - */ - public boolean compareAndSet(final AtomicRateLimiter.State current, final AtomicRateLimiter.State next) { - if (atomicRateLimiter.state.compareAndSet(current, next)) { - return true; - } else { - LockSupport.parkNanos(1); - return false; - } + public void reservePermissions(Blackhole bh) { + AtomicRateLimiter.State next = atomicRateLimiter.reservePermissions(0L, 31L, 1, 0L); + bh.consume(next); } @Benchmark @@ -112,16 +93,74 @@ public boolean compareAndSet(final AtomicRateLimiter.State current, final Atomic @Warmup(iterations = WARMUP_COUNT) @Fork(value = FORK_COUNT) @Measurement(iterations = ITERATION_COUNT) - public String semaphoreBasedPermission() { - return semaphoreGuardedSupplier.get(); + public void currentNanoTime(Blackhole bh) { + long next = atomicRateLimiter.currentNanoTime(); + bh.consume(next); } - @Benchmark - @Threads(value = THREAD_COUNT) - @Warmup(iterations = WARMUP_COUNT) - @Fork(value = FORK_COUNT) - @Measurement(iterations = ITERATION_COUNT) - public String atomicPermission() { - return atomicGuardedSupplier.get(); - } +// @Benchmark +// @Threads(value = THREAD_COUNT) +// @Warmup(iterations = WARMUP_COUNT) +// @Fork(value = FORK_COUNT) +// @Measurement(iterations = ITERATION_COUNT) +// public void mutex(Blackhole bh) { +// synchronized (mutex) { +// state = atomicRateLimiter.calculateNextState(Duration.ZERO.toNanos(), state); +// } +// } +// +// @Benchmark +// @Threads(value = THREAD_COUNT) +// @Warmup(iterations = WARMUP_COUNT) +// @Fork(value = FORK_COUNT) +// @Measurement(iterations = ITERATION_COUNT) +// public void atomic(Blackhole bh) { +// atomicRateLimiter.state.updateAndGet(state -> { +// return atomicRateLimiter.calculateNextState(Duration.ZERO.toNanos(), state); +// }); +// } +// +// @Benchmark +// @Threads(value = THREAD_COUNT) +// @Warmup(iterations = WARMUP_COUNT) +// @Fork(value = FORK_COUNT) +// @Measurement(iterations = ITERATION_COUNT) +// public void atomicBackOf(Blackhole bh) { +// AtomicRateLimiter.State prev; +// AtomicRateLimiter.State next; +// do { +// prev = atomicRateLimiter.state.get(); +// next = atomicRateLimiter.calculateNextState(Duration.ZERO.toNanos(), prev); +// } while (!compareAndSet(prev, next)); +// } +// +// /* +// https://arxiv.org/abs/1305.5800 https://dzone.com/articles/wanna-get-faster-wait-bit +// */ +// public boolean compareAndSet(final AtomicRateLimiter.State current, final AtomicRateLimiter.State next) { +// if (atomicRateLimiter.state.compareAndSet(current, next)) { +// return true; +// } else { +// LockSupport.parkNanos(1); +// return false; +// } +// } +// +// @Benchmark +// @Threads(value = THREAD_COUNT) +// @Warmup(iterations = WARMUP_COUNT) +// @Fork(value = FORK_COUNT) +// @Measurement(iterations = ITERATION_COUNT) +// public String semaphoreBasedPermission() { +// return semaphoreGuardedSupplier.get(); +// } +// +// @Benchmark +// @Threads(value = THREAD_COUNT) +// @Warmup(iterations = WARMUP_COUNT) +// @Fork(value = FORK_COUNT) +// @Measurement(iterations = ITERATION_COUNT) +// public String atomicPermission() { +// return atomicGuardedSupplier.get(); +// } } \ No newline at end of file diff --git a/src/jmh/java/javaslang/circuitbreaker/casWithBackOff.txt b/src/jmh/java/javaslang/circuitbreaker/casWithBackOff.txt deleted file mode 100644 index 8423672aea..0000000000 --- a/src/jmh/java/javaslang/circuitbreaker/casWithBackOff.txt +++ /dev/null @@ -1,6 +0,0 @@ -Benchmark Mode Cnt Score Error Units -RateLimiterBenchmark.atomic thrpt 10 8.016 ± 0.327 ops/us -RateLimiterBenchmark.atomicBackOf thrpt 10 14.104 ± 0.097 ops/us -RateLimiterBenchmark.atomicPermission thrpt 10 7.429 ± 0.299 ops/us -RateLimiterBenchmark.mutex thrpt 10 7.520 ± 0.304 ops/us -RateLimiterBenchmark.semaphoreBasedPermission thrpt 10 17.923 ± 6.043 ops/us diff --git a/src/jmh/java/javaslang/circuitbreaker/stateCalculationBenchmark.txt b/src/jmh/java/javaslang/circuitbreaker/stateCalculationBenchmark.txt new file mode 100644 index 0000000000..698c6561f5 --- /dev/null +++ b/src/jmh/java/javaslang/circuitbreaker/stateCalculationBenchmark.txt @@ -0,0 +1,5 @@ +Benchmark Mode Cnt Score Error Units +RateLimiterBenchmark.calculateNextState avgt 10 0.101 ± 0.008 us/op +RateLimiterBenchmark.currentNanoTime avgt 10 0.077 ± 0.001 us/op +RateLimiterBenchmark.nanosToWaitForPermission avgt 10 0.003 ± 0.001 us/op +RateLimiterBenchmark.reservePermissions avgt 10 0.007 ± 0.001 us/op diff --git a/src/main/java/javaslang/ratelimiter/internal/AtomicRateLimiter.java b/src/main/java/javaslang/ratelimiter/internal/AtomicRateLimiter.java index 2053824a0c..5e25faeb78 100644 --- a/src/main/java/javaslang/ratelimiter/internal/AtomicRateLimiter.java +++ b/src/main/java/javaslang/ratelimiter/internal/AtomicRateLimiter.java @@ -264,7 +264,7 @@ public long getAvailablePermissions() { /** * Created only for test purposes. Simply calls {@link System#nanoTime()} */ - private long currentNanoTime() { + public long currentNanoTime() { return nanoTime(); } }