RxJava并行计算

时间:2017-09-18 16:43:50

标签: java rx-java

我试图证明RxJava与顺序(我假设的)阻塞计算相比的性能。

我在看this postthis SO question。根据经验,使用System.currentTimeMillis()和Thread.sleep()进行基准测试在处理计算而不是I / O时不会产生一致的结果,所以我尝试设置一个简单的JMH基准测试。

我的基准计算两个整数并将它们加起来:

public class MyBenchmark {

    private Worker workerSequential;
    private Worker workerParallel;

    private int semiIntenseCalculation(int i) {
        Double d = Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(i)))))))))))))))));
        return d.intValue() + i;
    }

    private int nonIntenseCalculation(int i) {
        Double d = Math.tan(Math.atan(Math.tan(Math.atan(Math.tan(Math.tan(Math.atan(i)))))));
        return d.intValue() + i;
    }


    private Observable<Object> intensiveObservable() {
        return Observable.fromCallable(new Callable<Object>() {
            @Override
            public Object call() throws Exception {

                int randomNumforSemi = ThreadLocalRandom.current().nextInt(0, 101);
                Integer i =  semiIntenseCalculation(randomNumforSemi);
                int randomNumforNon = ThreadLocalRandom.current().nextInt(0, 101);
                Integer j = nonIntenseCalculation(randomNumforNon);

                return i+j;
            }
        });
    };

    private Observable<Object> semiIntensiveObservable() {
        return Observable.fromCallable(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                int randomNumforSemi = ThreadLocalRandom.current().nextInt(0, 101);
                return semiIntenseCalculation(randomNumforSemi);
            }
        });
    };

    private Observable<Object> nonIntensiveObservable() {
        return Observable.fromCallable(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                int randomNumforNon = ThreadLocalRandom.current().nextInt(0, 101);
                return nonIntenseCalculation(randomNumforNon);
            }
        });
    };

    public interface Worker {
        void work();
    }

    @Setup
    public void setup(final Blackhole bh) {

        workerSequential = new Worker() {

            @Override
            public void work() {


                Observable.just(intensiveObservable())
                  .subscribe(new Subscriber<Object>() {

                    @Override
                    public void onError(Throwable error) {

                    }

                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onNext(Object arg) {
                        bh.consume(arg);

                    }
                });
            }
        };

        workerParallel = new Worker() {
            @Override
            public void work() {
                Observable.zip(semiIntensiveObservable().subscribeOn(Schedulers.computation()),
                               nonIntensiveObservable().subscribeOn(Schedulers.computation()),
                               new Func2<Object, Object, Object>() {

                    @Override
                    public Object call(Object semiIntensive, Object nonIntensive) {
                        return (Integer)semiIntensive + (Integer)nonIntensive;
                    }

                }).subscribe(bh::consume);
            }
        };

    }

    @Benchmark
    public void calculateSequential() {
        workerSequential.work();
    }

    @Benchmark
    public void calculateParallel() {
        workerParallel.work();
    }
}

我对结果感到困惑:

# Run complete. Total time: 00:00:21

Benchmark                        Mode  Cnt      Score      Error  Units
MyBenchmark.calculateParallel    avgt    5  15602,176 ± 1663,650  ns/op
MyBenchmark.calculateSequential  avgt    5    288,128 ±    6,982  ns/op

显然我期待并行计算更快。 RxJava是否仅适用于并行I / O或为什么我会得到这些结果?

1 个答案:

答案 0 :(得分:1)

你做错了基准测试。您应该等待并行工作完成(否则通过blockingSubscribe),您将启动相当多的工作,这会增加显着的GC开销并且还会使执行程序的内部队列膨胀。

Here是衡量各种并行工作的参考基准。请注意,调度工作本身就有开销,除非在并行设置中每个工作项有500个周期,否则您可能看不到这种fork-join类型并行工作负载的任何改进。