diff --git a/tests/performance-jmh/README.md b/tests/performance-jmh/README.md new file mode 100644 index 0000000000..2dd4532316 --- /dev/null +++ b/tests/performance-jmh/README.md @@ -0,0 +1,41 @@ +Apache Artemix JMH Benchmarks +------- +This module contains optional [JMH](http://openjdk.java.net/projects/code-tools/jmh/) performance tests. + +Note that this module is an optional part of the overall project build and does not deploy anything, due to its use +of JMH which is not permissively licensed. The module must be built directly. + +Building the benchmarks +------- +The benchmarks are maven built and involve some code generation for the JMH part. As such it is required that you +rebuild upon changing the code. + + mvn clean install + +Running the benchmarks: General +------- +It is recommended that you consider some basic benchmarking practices before running benchmarks: + + 1. Use a quiet machine with enough CPUs to run the number of threads you mean to run. + 2. Set the CPU freq to avoid variance due to turbo boost/heating. + 3. Use an OS tool such as taskset to pin the threads in the topology you mean to measure. + +Running the JMH Benchmarks +----- +To run all JMH benchmarks: + + java -jar target/benchmark.jar + +To list available benchmarks: + + java -jar target/benchmark.jar -l +Some JMH help: + + java -jar target/benchmark.jar -h + +Example +----- +To run a benchmark on a single thread (tg 1) with gc profiling use: + + java -jar target/benchmark.jar -prof gc -tg 1 + diff --git a/tests/performance-jmh/pom.xml b/tests/performance-jmh/pom.xml new file mode 100644 index 0000000000..0ee556db5d --- /dev/null +++ b/tests/performance-jmh/pom.xml @@ -0,0 +1,99 @@ + + + + + org.apache.activemq.tests + artemis-tests-pom + 2.17.0-SNAPSHOT + + 4.0.0 + + performance-jmh + jar + + ActiveMQ Artemis JMH Performance Tests + + + 1.26 + + + + + com.google.errorprone + error_prone_core + + + org.apache.activemq + artemis-core-client + ${project.version} + + + org.apache.activemq + artemis-server + ${project.version} + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + provided + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + benchmark + + + org.openjdk.jmh.Main + + + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + + + diff --git a/tests/performance-jmh/src/main/java/org/apache/activemq/artemis/tests/performance/jmh/WildcardAddressManagerPerfTest.java b/tests/performance-jmh/src/main/java/org/apache/activemq/artemis/tests/performance/jmh/WildcardAddressManagerPerfTest.java new file mode 100644 index 0000000000..28392822de --- /dev/null +++ b/tests/performance-jmh/src/main/java/org/apache/activemq/artemis/tests/performance/jmh/WildcardAddressManagerPerfTest.java @@ -0,0 +1,247 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.activemq.artemis.tests.performance.jmh; + +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.activemq.artemis.api.core.Message; +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.config.WildcardConfiguration; +import org.apache.activemq.artemis.core.filter.Filter; +import org.apache.activemq.artemis.core.postoffice.Binding; +import org.apache.activemq.artemis.core.postoffice.BindingType; +import org.apache.activemq.artemis.core.postoffice.Bindings; +import org.apache.activemq.artemis.core.postoffice.BindingsFactory; +import org.apache.activemq.artemis.core.postoffice.impl.BindingsImpl; +import org.apache.activemq.artemis.core.postoffice.impl.WildcardAddressManager; +import org.apache.activemq.artemis.core.server.Bindable; +import org.apache.activemq.artemis.core.server.RoutingContext; +import org.apache.activemq.artemis.core.server.impl.AddressInfo; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Group; +import org.openjdk.jmh.annotations.GroupThreads; +import org.openjdk.jmh.annotations.Measurement; +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.Warmup; + +@State(Scope.Benchmark) +@Fork(2) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 8, time = 1) +public class WildcardAddressManagerPerfTest { + + private static class BindingFactoryFake implements BindingsFactory { + + @Override + public Bindings createBindings(SimpleString address) throws Exception { + return new BindingsImpl(address, null); + } + } + + private static class BindingFake implements Binding { + + final SimpleString address; + final SimpleString id; + final Long idl; + + BindingFake(SimpleString addressParameter, SimpleString id, long idl) { + this.address = addressParameter; + this.id = id; + this.idl = idl; + } + + @Override + public void unproposed(SimpleString groupID) { + + } + + @Override + public SimpleString getAddress() { + return address; + } + + @Override + public Bindable getBindable() { + return null; + } + + @Override + public BindingType getType() { + return BindingType.LOCAL_QUEUE; + } + + @Override + public SimpleString getUniqueName() { + return id; + } + + @Override + public SimpleString getRoutingName() { + return id; + } + + @Override + public SimpleString getClusterName() { + return null; + } + + @Override + public Filter getFilter() { + return null; + } + + @Override + public boolean isHighAcceptPriority(Message message) { + return false; + } + + @Override + public boolean isExclusive() { + return false; + } + + @Override + public Long getID() { + return idl; + } + + @Override + public int getDistance() { + return 0; + } + + @Override + public void route(Message message, RoutingContext context) throws Exception { + } + + @Override + public void close() throws Exception { + } + + @Override + public String toManagementString() { + return "FakeBiding Address=" + this.address; + } + + @Override + public boolean isConnected() { + return true; + } + + @Override + public void routeWithAck(Message message, RoutingContext context) { + + } + } + + public WildcardAddressManager addressManager; + + @Param({"2", "8", "10"}) + int topicsLog2; + int topics; + AtomicLong topicCounter; + private static final WildcardConfiguration WILDCARD_CONFIGURATION; + SimpleString[] addresses; + + static { + WILDCARD_CONFIGURATION = new WildcardConfiguration(); + WILDCARD_CONFIGURATION.setAnyWords('>'); + } + + private static final SimpleString WILDCARD = SimpleString.toSimpleString("Topic1.>"); + + @Setup + public void init() throws Exception { + addressManager = new WildcardAddressManager(new BindingFactoryFake(), WILDCARD_CONFIGURATION, null, null); + + addressManager.addAddressInfo(new AddressInfo(WILDCARD, RoutingType.MULTICAST)); + + topics = 1 << topicsLog2; + addresses = new SimpleString[topics]; + for (int i = 0; i < topics; i++) { + Binding binding = new BindingFake(WILDCARD, SimpleString.toSimpleString("" + i), i); + addressManager.addBinding(binding); + addresses[i] = SimpleString.toSimpleString("Topic1." + i); + addressManager.getBindingsForRoutingAddress(addresses[i]); + } + topicCounter = new AtomicLong(0); + topicCounter.set(topics); + } + + private long nextId() { + return topicCounter.getAndIncrement(); + } + + @State(value = Scope.Thread) + public static class ThreadState { + + Binding binding; + long next; + SimpleString[] addresses; + + @Setup + public void init(WildcardAddressManagerPerfTest benchmarkState) { + final long id = benchmarkState.nextId(); + binding = new BindingFake(WILDCARD, SimpleString.toSimpleString("" + id), id); + addresses = benchmarkState.addresses; + } + + public SimpleString nextAddress() { + final long current = next; + next = current + 1; + final int index = (int) (current & (addresses.length - 1)); + return addresses[index]; + } + } + + @Benchmark + @Group("both") + @GroupThreads(2) + public Bindings testPublishWhileAddRemoveNewBinding(ThreadState state) throws Exception { + return addressManager.getBindingsForRoutingAddress(state.nextAddress()); + } + + @Benchmark + @Group("both") + @GroupThreads(2) + public Binding testAddRemoveNewBindingWhilePublish(ThreadState state) throws Exception { + final Binding binding = state.binding; + addressManager.addBinding(binding); + return addressManager.removeBinding(binding.getUniqueName(), null); + } + + @Benchmark + @GroupThreads(4) + public Bindings testJustPublish(ThreadState state) throws Exception { + return addressManager.getBindingsForRoutingAddress(state.nextAddress()); + } + + @Benchmark + @GroupThreads(4) + public Binding testJustAddRemoveNewBinding(ThreadState state) throws Exception { + final Binding binding = state.binding; + addressManager.addBinding(binding); + return addressManager.removeBinding(binding.getUniqueName(), null); + } + +} + diff --git a/tests/pom.xml b/tests/pom.xml index c11b41f43f..fa486c1651 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -90,6 +90,12 @@ extra-tests + + jmh + + performance-jmh + + activemq5-unit-tests