1/*
2 * Copyright (C) 2014 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.util.concurrent;
18
19import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
20import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService;
21
22import com.google.caliper.AfterExperiment;
23import com.google.caliper.BeforeExperiment;
24import com.google.caliper.Benchmark;
25import com.google.caliper.Param;
26import com.google.caliper.api.Footprint;
27import com.google.caliper.api.VmOptions;
28
29import java.util.HashSet;
30import java.util.Set;
31import java.util.concurrent.Executor;
32import java.util.concurrent.atomic.AtomicInteger;
33
34/**
35 * A benchmark comparing the {@link MoreExecutors#newDirectExecutorService()} to
36 * {@link MoreExecutors#directExecutor}.
37 */
38@VmOptions({"-Xms12g", "-Xmx12g", "-d64"})
39public class MoreExecutorsDirectExecutorBenchmark {
40  enum Impl {
41    EXECUTOR_SERVICE {
42      @Override Executor executor() {
43        return newDirectExecutorService();
44      }
45    },
46    EXECUTOR {
47      @Override Executor executor() {
48        return directExecutor();
49      }
50    };
51    abstract Executor executor();
52  }
53
54  @Param Impl impl;
55  Executor executor;
56
57  static final class CountingRunnable implements Runnable {
58    AtomicInteger integer = new AtomicInteger();
59    @Override public void run() {
60      integer.incrementAndGet();
61    }
62  }
63
64  CountingRunnable countingRunnable = new CountingRunnable();
65
66  Set<Thread> threads = new HashSet<Thread>();
67
68  @BeforeExperiment void before() {
69    executor = impl.executor();
70    for (int i = 0; i < 4; i++) {
71      Thread thread = new Thread() {
72        @Override public void run() {
73          CountingRunnable localRunnable = new CountingRunnable();
74          while (!isInterrupted()) {
75            executor.execute(localRunnable);
76          }
77          countingRunnable.integer.addAndGet(localRunnable.integer.get());
78        }
79      };
80      threads.add(thread);
81    }
82  }
83
84  @AfterExperiment void after() {
85    for (Thread thread : threads) {
86      thread.interrupt();  // try to get them to exit
87    }
88    threads.clear();
89  }
90
91  @Footprint Object measureSize() {
92    return executor;
93  }
94
95  @Benchmark int timeUncontendedExecute(int reps) {
96    final Executor executor = this.executor;
97    final CountingRunnable countingRunnable = this.countingRunnable;
98    for (int i = 0; i < reps; i++) {
99      executor.execute(countingRunnable);
100    }
101    return countingRunnable.integer.get();
102  }
103
104  @Benchmark int timeContendedExecute(int reps) {
105    final Executor executor = this.executor;
106    for (Thread thread : threads) {
107      if (!thread.isAlive()) {
108        thread.start();
109      }
110    }
111    final CountingRunnable countingRunnable = this.countingRunnable;
112    for (int i = 0; i < reps; i++) {
113      executor.execute(countingRunnable);
114    }
115    return countingRunnable.integer.get();
116  }
117}
118