/* * 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.throughput.channels; import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; import org.jctools.channels.Channel; import org.jctools.channels.ChannelConsumer; import org.jctools.channels.ChannelProducer; import org.jctools.channels.ChannelReceiver; import org.jctools.channels.mpsc.MpscChannel; import org.jctools.channels.spsc.SpscChannel; import org.jctools.util.JvmInfo; import org.jctools.util.Pow2; import org.openjdk.jmh.annotations.AuxCounters; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Group; 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; import org.openjdk.jmh.infra.Blackhole; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; @State(Scope.Group) @BenchmarkMode(Mode.Throughput) @OutputTimeUnit(TimeUnit.MICROSECONDS) @Warmup(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS) public class ChannelThroughputBackoffNone { private static final long DELAY_PRODUCER = Long.getLong("delay.p", 0L); private static final long DELAY_CONSUMER = Long.getLong("delay.c", 0L); @Param(value = { "132000" }) int capacity; public enum Type{ Spsc,Mpsc } @Param(value = { "Spsc", "Mpsc" }) Type type; private ByteBuffer buffer; private Channel<Ping> channel; private ChannelProducer<Ping> producer; private ChannelConsumer consumer; private ChannelReceiver<Ping> receiver; // Deliberately not a local to avoid constant folding. private long writeValue = 1L; @Setup public void setup(final Blackhole blackhole) { receiver = new ChannelReceiver<Ping>() { @Override public void accept(Ping element) { blackhole.consume(element.getValue()); } }; buffer = ByteBuffer .allocateDirect(Pow2.roundToPowerOfTwo(capacity * 2) * (8 + 4) + JvmInfo.CACHE_LINE_SIZE * 5); switch (type) { case Spsc: channel = new SpscChannel<Ping>(buffer, capacity, Ping.class); break; case Mpsc: channel = new MpscChannel<Ping>(buffer, capacity, Ping.class); break; default: throw new IllegalArgumentException(); } producer = channel.producer(); consumer = channel.consumer(receiver); OfferCounters oc = new OfferCounters(); PollCounters pc = new PollCounters(); for (int i = 0; i < 100000; i++) { offer(oc); poll(pc, null); } } @AuxCounters @State(Scope.Thread) public static class PollCounters { public int pollsFailed; public int pollsMade; @Setup(Level.Iteration) public void clean() { pollsFailed = pollsMade = 0; } } @AuxCounters @State(Scope.Thread) public static class OfferCounters { public int offersFailed; public int offersMade; @Setup(Level.Iteration) public void clean() { offersFailed = offersMade = 0; } } private static ThreadLocal<Object> marker = new ThreadLocal<Object>(); @State(Scope.Thread) public static class ConsumerMarker { public ConsumerMarker() { marker.set(this); } } @Benchmark @Group("tpt") public void offer(OfferCounters counters) { ChannelProducer<Ping> lProducer = producer; if (!lProducer.claim()) { counters.offersFailed++; } else { Ping element = lProducer.currentElement(); element.setValue(writeValue); lProducer.commit(); counters.offersMade++; } if (DELAY_PRODUCER != 0) { Blackhole.consumeCPU(DELAY_PRODUCER); } } @Benchmark @Group("tpt") public void poll(PollCounters counters, ConsumerMarker cm) { if (!consumer.read()) { counters.pollsFailed++; } else { counters.pollsMade++; } if (DELAY_CONSUMER != 0) { Blackhole.consumeCPU(DELAY_CONSUMER); } } @TearDown(Level.Iteration) public void emptyQ() { if (marker.get() == null) return; // sadly the iteration tear down is performed from each participating thread, so we need to guess // which is which (can't have concurrent access to poll). while (consumer.read()) ; } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder().forks(0) .include(ChannelThroughputBackoffNone.class.getSimpleName()).param("type", "Mpsc") .build(); new Runner(opt).run(); } }