├── README
├── pom.xml
└── src
└── main
└── java
└── net
└── shipilev
├── Disruptor.java
├── ForkJoin.java
├── ForkJoinRecursive.java
├── ForkJoinRecursiveDeep.java
├── ForkJoinReuse.java
├── SingleThread.java
├── Streams.java
└── Workload.java
/README:
--------------------------------------------------------------------------------
1 | (Implicitly uses JMH)
2 |
3 | Use JDK 8 GA to build and run.
4 |
5 | Build:
6 | $ mvn clean install
7 |
8 | Run:
9 | $ java -jar target/microbenchmarks.jar
10 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
25 |
27 | 4.0.0
28 | net.shipilev
29 | disrupting-fjp
30 | 1.0-SNAPSHOT
31 | jar
32 |
33 |
34 | org.openjdk.jmh
35 | jmh-core
36 | 1.6.2
37 |
38 |
39 | org.openjdk.jmh
40 | jmh-generator-annprocess
41 | 1.6.2
42 | provided
43 |
44 |
45 | com.lmax
46 | disruptor
47 | 3.3.2
48 |
49 |
50 |
51 | UTF-8
52 |
53 |
54 |
55 |
56 | org.apache.maven.plugins
57 | maven-compiler-plugin
58 | 3.0
59 |
60 | 1.8
61 | 1.8
62 | 1.8
63 |
64 |
65 |
66 | org.apache.maven.plugins
67 | maven-shade-plugin
68 | 2.0
69 |
70 |
71 | package
72 |
73 | shade
74 |
75 |
76 | microbenchmarks
77 |
78 |
79 | org.openjdk.jmh.Main
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/src/main/java/net/shipilev/Disruptor.java:
--------------------------------------------------------------------------------
1 | package net.shipilev;
2 |
3 | import com.lmax.disruptor.EventFactory;
4 | import com.lmax.disruptor.EventHandler;
5 | import com.lmax.disruptor.RingBuffer;
6 | import com.lmax.disruptor.SleepingWaitStrategy;
7 | import com.lmax.disruptor.dsl.ProducerType;
8 | import org.openjdk.jmh.annotations.Benchmark;
9 | import org.openjdk.jmh.annotations.Level;
10 | import org.openjdk.jmh.annotations.Setup;
11 | import org.openjdk.jmh.annotations.TearDown;
12 |
13 | import java.util.concurrent.CountDownLatch;
14 | import java.util.concurrent.ExecutorService;
15 | import java.util.concurrent.Executors;
16 |
17 | public class Disruptor extends Workload {
18 |
19 | private ExecutorService executor;
20 | private com.lmax.disruptor.dsl.Disruptor disruptor;
21 | private RingBuffer ringBuffer;
22 | private PiResultReclaimer result;
23 |
24 | public static class PiJob {
25 | public double result;
26 | public int sliceNr;
27 | public int partitionId;
28 |
29 | public void calculatePi() {
30 | result = doCalculatePi(sliceNr);
31 | }
32 | }
33 |
34 | public static class PiEventFac implements EventFactory {
35 |
36 | @Override
37 | public PiJob newInstance() {
38 | return new PiJob();
39 | }
40 | }
41 |
42 | public static class PiEventProcessor implements EventHandler {
43 | private final int partionId;
44 |
45 | public PiEventProcessor(final int partionId) {
46 | this.partionId = partionId;
47 | }
48 |
49 | @Override
50 | public void onEvent(final PiJob event, final long sequence, final boolean isEndOfBatch) throws Exception {
51 | if (partionId == event.partitionId) {
52 | event.calculatePi();
53 | }
54 | }
55 | }
56 |
57 | public static class PiResultReclaimer implements EventHandler {
58 | private double result;
59 | private long seq;
60 | private final int numSlice;
61 | private final CountDownLatch latch;
62 |
63 | public PiResultReclaimer(final int numSlice) {
64 | this.numSlice = numSlice;
65 | latch = new CountDownLatch(1);
66 | }
67 |
68 | @Override
69 | public void onEvent(final PiJob event, final long sequence, final boolean isEndOfBatch) throws Exception {
70 | result += event.result;
71 | if (++seq >= numSlice) {
72 | latch.countDown();
73 | }
74 | }
75 |
76 | public double get() throws InterruptedException {
77 | latch.await();
78 | return result;
79 | }
80 | }
81 |
82 | @Setup(Level.Iteration)
83 | public void setup() {
84 | executor = Executors.newCachedThreadPool();
85 | disruptor = new com.lmax.disruptor.dsl.Disruptor<>(new PiEventFac(), Integer.highestOneBit(getSlices()),
86 | executor, ProducerType.SINGLE, new SleepingWaitStrategy());
87 | final PiEventProcessor procs[] = new PiEventProcessor[getThreads()];
88 | result = new PiResultReclaimer(getSlices());
89 | for (int i = 0; i < procs.length; i++) {
90 | procs[i] = new PiEventProcessor(i);
91 | }
92 | disruptor.handleEventsWith(procs).then(result);
93 | disruptor.start();
94 | ringBuffer = disruptor.getRingBuffer();
95 | }
96 |
97 | @TearDown(Level.Iteration)
98 | public void tearDown() {
99 | disruptor.shutdown();
100 | executor.shutdownNow();
101 | }
102 |
103 | @Benchmark
104 | public double run() throws InterruptedException {
105 | int ts = getThreads();
106 | int slices = getSlices();
107 | int partitionId = 0;
108 | for (int i = 0; i < slices; i++) {
109 | final long seq = ringBuffer.next();
110 | final PiJob piJob = ringBuffer.get(seq);
111 | piJob.sliceNr = i;
112 | piJob.result = 0;
113 | piJob.partitionId = partitionId;
114 | ringBuffer.publish(seq);
115 | partitionId = (partitionId == (ts - 1)) ? 0 : partitionId + 1;
116 | }
117 | return result.get();
118 | }
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/src/main/java/net/shipilev/ForkJoin.java:
--------------------------------------------------------------------------------
1 | package net.shipilev;
2 |
3 | import org.openjdk.jmh.annotations.Benchmark;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 | import java.util.concurrent.RecursiveTask;
8 |
9 | public class ForkJoin extends Workload {
10 |
11 | /*
12 | The fork-join task below is used as "just" the Callable.
13 | */
14 |
15 | static class PiForkJoinTask extends RecursiveTask {
16 | private final int from;
17 | private final int to;
18 |
19 | public PiForkJoinTask(final int from, final int to) {
20 | this.from = from;
21 | this.to = to;
22 | }
23 |
24 | @Override
25 | protected Double compute() {
26 | double acc = 0;
27 | for (int s = from; s < to; s++) {
28 | acc += doCalculatePi(s);
29 | }
30 | return acc;
31 | }
32 | }
33 |
34 | @Benchmark
35 | public double run() throws InterruptedException {
36 | final List tasks = new ArrayList();
37 | final int ts = getThreads();
38 | final int slicePerThread = getSlices() / ts;
39 | for (int i = 0; i < ts; i++) {
40 | PiForkJoinTask task = new PiForkJoinTask(i * slicePerThread, (i + 1) * slicePerThread);
41 | task.fork();
42 | tasks.add(task);
43 | }
44 | double acc = 0;
45 | for (PiForkJoinTask task : tasks) {
46 | acc += task.join();
47 | }
48 | return acc;
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/net/shipilev/ForkJoinRecursive.java:
--------------------------------------------------------------------------------
1 | package net.shipilev;
2 |
3 | import org.openjdk.jmh.annotations.Benchmark;
4 |
5 | import java.util.concurrent.ForkJoinTask;
6 | import java.util.concurrent.RecursiveTask;
7 |
8 | public class ForkJoinRecursive extends Workload {
9 |
10 | /*
11 | The fork-join task below recursively divides the work on slices,
12 | with some sensible decomposition threshold.
13 | */
14 |
15 | static class PiForkJoinTask extends RecursiveTask {
16 | private final int from;
17 | private final int to;
18 |
19 | public PiForkJoinTask(final int from, final int to) {
20 | this.from = from;
21 | this.to = to;
22 | }
23 |
24 | @Override
25 | protected Double compute() {
26 | final int slices = to - from;
27 | if (slices < 10000) {
28 | double acc = 0;
29 | for (int s = from; s < to; s++) {
30 | acc += doCalculatePi(s);
31 | }
32 | return acc;
33 | }
34 | final int mid = from + slices / 2;
35 | PiForkJoinTask t1 = new PiForkJoinTask(from, mid);
36 | PiForkJoinTask t2 = new PiForkJoinTask(mid, to);
37 | ForkJoinTask.invokeAll(t1, t2);
38 | return t1.join() + t2.join();
39 | }
40 | }
41 |
42 | @Benchmark
43 | public double run() throws InterruptedException {
44 | return new PiForkJoinTask(0, getSlices()).invoke();
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/net/shipilev/ForkJoinRecursiveDeep.java:
--------------------------------------------------------------------------------
1 | package net.shipilev;
2 |
3 | import org.openjdk.jmh.annotations.Benchmark;
4 |
5 | import java.util.concurrent.ForkJoinTask;
6 | import java.util.concurrent.RecursiveTask;
7 |
8 | public class ForkJoinRecursiveDeep extends Workload {
9 |
10 | /*
11 | The fork-join task below deeply recurses, up until the leaf
12 | contains a single slice.
13 | */
14 |
15 | static class PiForkJoinTask extends RecursiveTask {
16 | private final int from;
17 | private final int to;
18 |
19 | public PiForkJoinTask(final int from, final int to) {
20 | this.from = from;
21 | this.to = to;
22 | }
23 |
24 | @Override
25 | protected Double compute() {
26 | final int slices = to - from;
27 | if (slices <= 1) {
28 | double acc = 0;
29 | for (int s = from; s < to; s++) {
30 | acc += doCalculatePi(s);
31 | }
32 | return acc;
33 | }
34 | final int mid = from + slices / 2;
35 | PiForkJoinTask t1 = new PiForkJoinTask(from, mid);
36 | PiForkJoinTask t2 = new PiForkJoinTask(mid, to);
37 | ForkJoinTask.invokeAll(t1, t2);
38 | return t1.join() + t2.join();
39 | }
40 | }
41 |
42 | @Benchmark
43 | public double run() throws InterruptedException {
44 | return new PiForkJoinTask(0, getSlices()).invoke();
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/net/shipilev/ForkJoinReuse.java:
--------------------------------------------------------------------------------
1 | package net.shipilev;
2 |
3 | import org.openjdk.jmh.annotations.Benchmark;
4 |
5 | import java.util.concurrent.RecursiveTask;
6 |
7 | public class ForkJoinReuse extends Workload {
8 |
9 | /*
10 | The fork-join task below is used as "just" the Callable,
11 | and "reuses" the submitted tasks.
12 | */
13 |
14 | static class PiForkJoinTask extends RecursiveTask {
15 | private int slice;
16 |
17 | @Override
18 | protected Double compute() {
19 | return doCalculatePi(slice);
20 | }
21 | }
22 |
23 | @Benchmark
24 | public double run() throws InterruptedException {
25 | final int stride = getThreads() * 100;
26 | final PiForkJoinTask[] tasks = new PiForkJoinTask[stride];
27 | for (int i = 0; i < stride; i++) {
28 | PiForkJoinTask task = new PiForkJoinTask();
29 | task.slice = i;
30 | task.fork();
31 | tasks[i] = task;
32 | }
33 | double acc = 0;
34 | final int s1 = getSlices() / stride;
35 | for (int i = 1; i < s1; i++) {
36 | for (PiForkJoinTask task : tasks) {
37 | acc += task.join();
38 | task.reinitialize();
39 | task.slice += stride;
40 | task.fork();
41 | }
42 | }
43 | for (PiForkJoinTask task : tasks) {
44 | acc += task.join();
45 | }
46 | final int s2 = getSlices() % stride;
47 | for (int i = 0; i < s2; i++) {
48 | final PiForkJoinTask task = tasks[i];
49 | task.reinitialize();
50 | task.slice += stride;
51 | task.fork();
52 | }
53 | for (int i = 0; i < s2; i++) {
54 | acc += tasks[i].join();
55 | }
56 | return acc;
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/net/shipilev/SingleThread.java:
--------------------------------------------------------------------------------
1 | package net.shipilev;
2 |
3 | import org.openjdk.jmh.annotations.Benchmark;
4 |
5 | public class SingleThread extends Workload {
6 |
7 | @Benchmark
8 | public double run() {
9 | double acc = 0;
10 | for (int s = 0; s < getSlices(); s++) {
11 | acc += doCalculatePi(s);
12 | }
13 | return acc;
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/net/shipilev/Streams.java:
--------------------------------------------------------------------------------
1 | package net.shipilev;
2 |
3 | import org.openjdk.jmh.annotations.Benchmark;
4 |
5 | import java.util.stream.IntStream;
6 |
7 | public class Streams extends Workload {
8 |
9 | @Benchmark
10 | public double run() {
11 | return IntStream.range(0, getSlices())
12 | .parallel()
13 | .mapToDouble(Workload::doCalculatePi)
14 | .reduce(0, Double::sum);
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/net/shipilev/Workload.java:
--------------------------------------------------------------------------------
1 | package net.shipilev;
2 |
3 | import org.openjdk.jmh.annotations.BenchmarkMode;
4 | import org.openjdk.jmh.annotations.Fork;
5 | import org.openjdk.jmh.annotations.Measurement;
6 | import org.openjdk.jmh.annotations.Mode;
7 | import org.openjdk.jmh.annotations.OutputTimeUnit;
8 | import org.openjdk.jmh.annotations.Param;
9 | import org.openjdk.jmh.annotations.Scope;
10 | import org.openjdk.jmh.annotations.State;
11 | import org.openjdk.jmh.annotations.Warmup;
12 |
13 | import java.util.concurrent.TimeUnit;
14 |
15 | @State(Scope.Benchmark)
16 | @Warmup(iterations = 5)
17 | @Measurement(iterations = 10)
18 | @Fork(5)
19 | @BenchmarkMode(Mode.SingleShotTime)
20 | @OutputTimeUnit(TimeUnit.MILLISECONDS)
21 | public class Workload {
22 |
23 | @Param({"500", "1000", "5000", "10000", "50000"})
24 | public static int slicesK;
25 |
26 | @Param({"10"})
27 | public static int workMult;
28 |
29 | @Param("0")
30 | public static int threads;
31 |
32 | static int getThreads() {
33 | if (threads == 0) {
34 | return Runtime.getRuntime().availableProcessors();
35 | } else {
36 | return threads;
37 | }
38 | }
39 |
40 | static int getSlices() {
41 | return slicesK * 1000;
42 | }
43 |
44 | public static double doCalculatePi(final int sliceNr) {
45 | final int from = sliceNr * workMult;
46 | final int to = from + workMult;
47 | final int c = (to << 1) + 1;
48 | double acc = 0;
49 | for (int a = 4 - ((from & 1) << 3), b = (from << 1) + 1; b < c; a = -a, b += 2) {
50 | acc += ((double) a) / b;
51 | }
52 | return acc;
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------