/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jctools.jmh.latency; import java.util.Queue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.jctools.queues.MessagePassingQueue; import org.jctools.queues.MessagePassingQueueByTypeFactory; import org.jctools.queues.QueueByTypeFactory; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Level; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OutputTimeUnit; import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; import org.openjdk.jmh.annotations.Warmup; @State(Scope.Benchmark) @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) @SuppressWarnings("serial") public class MpqBurstCost { abstract static class AbstractEvent extends AtomicBoolean { abstract void handle(); } static class Go extends AbstractEvent { @Override void handle() { // do nothing } } @State(Scope.Thread) public static class Stop extends AbstractEvent { @Override void handle() { lazySet(true); } } static class Consumer implements Runnable { final MessagePassingQueue<AbstractEvent> q; volatile boolean isRunning = true; public Consumer(MessagePassingQueue<AbstractEvent> q) { this.q = q; } @Override public void run() { final MessagePassingQueue<AbstractEvent> q = this.q; while (isRunning) { AbstractEvent e = null; if ((e = q.relaxedPoll()) == null) { continue; } e.handle(); } } } static final Go GO = new Go(); @Param({ "SpscArrayQueue", "MpscArrayQueue", "SpmcArrayQueue", "MpmcArrayQueue" }) String qType; @Param({ "100" }) int burstSize; @Param("1") int consumerCount; MessagePassingQueue<AbstractEvent> q; private Thread[] consumerThreads; private Consumer consumer; @Setup(Level.Trial) public void setupQueueAndConsumers() { q = MessagePassingQueueByTypeFactory.createQueue(qType, 128); // stretch the queue to the limit, working through resizing and full for (int i = 0; i < 128 + 100; i++) { q.offer(GO); } for (int i = 0; i < 128 + 100; i++) { q.poll(); } // make sure the important common case is exercised for (int i = 0; i < 20000; i++) { q.offer(GO); q.poll(); } q = MessagePassingQueueByTypeFactory.createQueue(qType, 128 * 1024); consumer = new Consumer(q); consumerThreads = new Thread[consumerCount]; for (int i = 0; i < consumerCount; i++) { consumerThreads[i] = new Thread(consumer); consumerThreads[i].start(); } } @Benchmark public void burstCost(Stop stop) { final int burst = burstSize; final MessagePassingQueue<AbstractEvent> q = this.q; final Go go = GO; stop.lazySet(false); for (int i = 0; i < burst - 1; i++) { while (!q.offer(go)) ; } while (!q.offer(stop)) ; while (!stop.get()) ; } @TearDown public void killConsumer() throws InterruptedException { consumer.isRunning = false; for (int i = 0; i < consumerCount; i++) { consumerThreads[i].join(); } } }