From a4530635bd6206ab00ef8851bec33e1a6d42e0da Mon Sep 17 00:00:00 2001 From: Matt Jacobs Date: Mon, 11 Jan 2016 20:35:52 -0800 Subject: [PATCH 1/3] Add blackhole to baseline tests for better comparisons. Removed tests of queue/observe/toObservable --- .../perf/CommandExecutionPerfTest.java | 74 ++++++++++--------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/hystrix-core/src/jmh/java/com/netflix/hystrix/perf/CommandExecutionPerfTest.java b/hystrix-core/src/jmh/java/com/netflix/hystrix/perf/CommandExecutionPerfTest.java index a077e48ab..f2eb00ae6 100644 --- a/hystrix-core/src/jmh/java/com/netflix/hystrix/perf/CommandExecutionPerfTest.java +++ b/hystrix-core/src/jmh/java/com/netflix/hystrix/perf/CommandExecutionPerfTest.java @@ -33,6 +33,7 @@ import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.infra.Blackhole; import rx.Observable; +import rx.functions.Func0; import rx.schedulers.Schedulers; import java.util.List; @@ -42,6 +43,10 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +/** + * Note that the hystrixExecute test must be run on a forked JVM. Otherwise, the initial properties that get + * set for the command apply to all runs. This would leave the command as THREAD-isolated in all test, for example. + */ public class CommandExecutionPerfTest { static HystrixCommandProperties.Setter threadIsolatedCommandDefaults = HystrixCommandProperties.Setter() @@ -68,6 +73,13 @@ private static HystrixCommandProperties.Setter getCommandSetter(HystrixCommandPr } } + @State(Scope.Thread) + public static class BlackholeState { + //amount of "work" to give to CPU + @Param({"1", "100", "10000"}) + public int blackholeConsumption; + } + @State(Scope.Thread) public static class CommandState { HystrixCommand command; @@ -75,6 +87,7 @@ public static class CommandState { @Param({"THREAD", "SEMAPHORE"}) public HystrixCommandProperties.ExecutionIsolationStrategy isolationStrategy; + //amount of "work" to give to CPU @Param({"1", "100", "10000"}) public int blackholeConsumption; @@ -135,18 +148,20 @@ public void tearDown() { @Benchmark @BenchmarkMode({Mode.Throughput}) @OutputTimeUnit(TimeUnit.MILLISECONDS) - public Integer baselineExecute() { + public Integer baselineExecute(BlackholeState bhState) { + Blackhole.consumeCPU(bhState.blackholeConsumption); return 1; } @Benchmark @BenchmarkMode({Mode.Throughput}) @OutputTimeUnit(TimeUnit.MILLISECONDS) - public Integer baselineQueue(ExecutorState state) throws InterruptedException, ExecutionException{ + public Integer baselineQueue(ExecutorState state, final BlackholeState bhState) throws InterruptedException, ExecutionException{ try { return state.executorService.submit(new Callable() { @Override public Integer call() throws Exception { + Blackhole.consumeCPU(bhState.blackholeConsumption); return 1; } }).get(); @@ -158,8 +173,14 @@ public Integer call() throws Exception { @Benchmark @BenchmarkMode({Mode.Throughput}) @OutputTimeUnit(TimeUnit.MILLISECONDS) - public Integer baselineSyncObserve() throws InterruptedException { - Observable syncObservable = Observable.just(1); + public Integer baselineSyncObserve(final BlackholeState bhState) throws InterruptedException { + Observable syncObservable = Observable.defer(new Func0>() { + @Override + public Observable call() { + Blackhole.consumeCPU(bhState.blackholeConsumption); + return Observable.just(1); + } + }); try { return syncObservable.toBlocking().first(); @@ -171,8 +192,14 @@ public Integer baselineSyncObserve() throws InterruptedException { @Benchmark @BenchmarkMode({Mode.Throughput}) @OutputTimeUnit(TimeUnit.MILLISECONDS) - public Integer baselineAsyncComputationObserve() throws InterruptedException { - Observable asyncObservable = Observable.just(1).subscribeOn(Schedulers.computation()); + public Integer baselineAsyncComputationObserve(final BlackholeState bhState) throws InterruptedException { + Observable asyncObservable = Observable.defer(new Func0>() { + @Override + public Observable call() { + Blackhole.consumeCPU(bhState.blackholeConsumption); + return Observable.just(1); + } + }).subscribeOn(Schedulers.computation()); try { return asyncObservable.toBlocking().first(); @@ -184,8 +211,14 @@ public Integer baselineAsyncComputationObserve() throws InterruptedException { @Benchmark @BenchmarkMode({Mode.Throughput}) @OutputTimeUnit(TimeUnit.MILLISECONDS) - public Integer baselineAsyncCustomThreadPoolObserve(ThreadPoolState state) { - Observable asyncObservable = Observable.just(1).subscribeOn(state.hystrixThreadPool.getScheduler()); + public Integer baselineAsyncCustomThreadPoolObserve(ThreadPoolState state, final BlackholeState bhState) { + Observable asyncObservable = Observable.defer(new Func0>() { + @Override + public Observable call() { + Blackhole.consumeCPU(bhState.blackholeConsumption); + return Observable.just(1); + } + }).subscribeOn(state.hystrixThreadPool.getScheduler()); try { return asyncObservable.toBlocking().first(); } catch (Throwable t) { @@ -199,29 +232,4 @@ public Integer baselineAsyncCustomThreadPoolObserve(ThreadPoolState state) { public Integer hystrixExecute(CommandState state) { return state.command.execute(); } - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - @OutputTimeUnit(TimeUnit.MILLISECONDS) - public Integer hystrixQueue(CommandState state) { - try { - return state.command.queue().get(); - } catch (Throwable t) { - return 2; - } - } - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - @OutputTimeUnit(TimeUnit.MILLISECONDS) - public Integer hystrixObserve(CommandState state) { - return state.command.observe().toBlocking().first(); - } - - @Benchmark - @BenchmarkMode({Mode.Throughput}) - @OutputTimeUnit(TimeUnit.MILLISECONDS) - public Integer hystrixToObservable(CommandState state) { - return state.command.toObservable().toBlocking().first(); - } } From c9aa6f39a170fb8fb5bcfbf0f5e5366db7e901fc Mon Sep 17 00:00:00 2001 From: Matt Jacobs Date: Mon, 11 Jan 2016 20:50:08 -0800 Subject: [PATCH 2/3] Added jmh test for HystrixObservableCommand --- .../perf/CommandExecutionPerfTest.java | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/hystrix-core/src/jmh/java/com/netflix/hystrix/perf/CommandExecutionPerfTest.java b/hystrix-core/src/jmh/java/com/netflix/hystrix/perf/CommandExecutionPerfTest.java index f2eb00ae6..f3d2162cc 100644 --- a/hystrix-core/src/jmh/java/com/netflix/hystrix/perf/CommandExecutionPerfTest.java +++ b/hystrix-core/src/jmh/java/com/netflix/hystrix/perf/CommandExecutionPerfTest.java @@ -18,6 +18,7 @@ import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandProperties; +import com.netflix.hystrix.HystrixObservableCommand; import com.netflix.hystrix.HystrixThreadPool; import com.netflix.hystrix.HystrixThreadPoolKey; import com.netflix.hystrix.HystrixThreadPoolProperties; @@ -66,6 +67,8 @@ public class CommandExecutionPerfTest { static HystrixThreadPoolProperties.Setter threadPoolDefaults = HystrixThreadPoolProperties.Setter() .withCoreSize(100); + static HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("PERF"); + private static HystrixCommandProperties.Setter getCommandSetter(HystrixCommandProperties.ExecutionIsolationStrategy isolationStrategy) { switch (isolationStrategy) { case THREAD: return threadIsolatedCommandDefaults; @@ -112,6 +115,34 @@ protected Integer getFallback() { } } + @State(Scope.Thread) + public static class ObservableCommandState { + HystrixObservableCommand command; + + //amount of "work" to give to CPU + @Param({"1", "100", "10000"}) + public int blackholeConsumption; + + @Setup(Level.Invocation) + public void setUp() { + command = new HystrixObservableCommand( + HystrixObservableCommand.Setter.withGroupKey(groupKey) + .andCommandPropertiesDefaults(getCommandSetter(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)) + ) { + @Override + protected Observable construct() { + return Observable.defer(new Func0>() { + @Override + public Observable call() { + Blackhole.consumeCPU(blackholeConsumption); + return Observable.just(1); + } + }); + } + }; + } + } + @State(Scope.Benchmark) public static class ExecutorState { ExecutorService executorService; @@ -156,7 +187,7 @@ public Integer baselineExecute(BlackholeState bhState) { @Benchmark @BenchmarkMode({Mode.Throughput}) @OutputTimeUnit(TimeUnit.MILLISECONDS) - public Integer baselineQueue(ExecutorState state, final BlackholeState bhState) throws InterruptedException, ExecutionException{ + public Integer baselineQueue(ExecutorState state, final BlackholeState bhState) throws InterruptedException, ExecutionException { try { return state.executorService.submit(new Callable() { @Override @@ -232,4 +263,11 @@ public Observable call() { public Integer hystrixExecute(CommandState state) { return state.command.execute(); } + + @Benchmark + @BenchmarkMode({Mode.Throughput}) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + public Integer hystrixObserve(ObservableCommandState state) { + return state.command.observe().toBlocking().first(); + } } From d1a18eb413baadc591354bd9c65ccd4f46771538 Mon Sep 17 00:00:00 2001 From: Matt Jacobs Date: Mon, 11 Jan 2016 20:59:00 -0800 Subject: [PATCH 3/3] Measure the impact of setting up a HystrixRequestContext --- .../perf/CommandExecutionPerfTest.java | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/hystrix-core/src/jmh/java/com/netflix/hystrix/perf/CommandExecutionPerfTest.java b/hystrix-core/src/jmh/java/com/netflix/hystrix/perf/CommandExecutionPerfTest.java index f3d2162cc..962adf08d 100644 --- a/hystrix-core/src/jmh/java/com/netflix/hystrix/perf/CommandExecutionPerfTest.java +++ b/hystrix-core/src/jmh/java/com/netflix/hystrix/perf/CommandExecutionPerfTest.java @@ -22,6 +22,7 @@ import com.netflix.hystrix.HystrixThreadPool; import com.netflix.hystrix.HystrixThreadPoolKey; import com.netflix.hystrix.HystrixThreadPoolProperties; +import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Level; @@ -86,6 +87,10 @@ public static class BlackholeState { @State(Scope.Thread) public static class CommandState { HystrixCommand command; + HystrixRequestContext requestContext; + + @Param({"true", "false"}) + public boolean setUpRequestContext; @Param({"THREAD", "SEMAPHORE"}) public HystrixCommandProperties.ExecutionIsolationStrategy isolationStrategy; @@ -96,6 +101,10 @@ public static class CommandState { @Setup(Level.Invocation) public void setUp() { + if (setUpRequestContext) { + requestContext = HystrixRequestContext.initializeContext(); + } + command = new HystrixCommand( HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("PERF")) .andCommandPropertiesDefaults(getCommandSetter(isolationStrategy)) @@ -113,11 +122,22 @@ protected Integer getFallback() { } }; } + + @TearDown(Level.Invocation) + public void tearDown() { + if (setUpRequestContext) { + requestContext.shutdown(); + } + } } @State(Scope.Thread) public static class ObservableCommandState { HystrixObservableCommand command; + HystrixRequestContext requestContext; + + @Param({"true", "false"}) + public boolean setUpRequestContext; //amount of "work" to give to CPU @Param({"1", "100", "10000"}) @@ -125,6 +145,10 @@ public static class ObservableCommandState { @Setup(Level.Invocation) public void setUp() { + if (setUpRequestContext) { + requestContext = HystrixRequestContext.initializeContext(); + } + command = new HystrixObservableCommand( HystrixObservableCommand.Setter.withGroupKey(groupKey) .andCommandPropertiesDefaults(getCommandSetter(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)) @@ -141,6 +165,13 @@ public Observable call() { } }; } + + @TearDown(Level.Invocation) + public void tearDown() { + if (setUpRequestContext) { + requestContext.shutdown(); + } + } } @State(Scope.Benchmark) @@ -175,7 +206,6 @@ public void tearDown() { } } - @Benchmark @BenchmarkMode({Mode.Throughput}) @OutputTimeUnit(TimeUnit.MILLISECONDS)