diff options
author | Maros Marsalek <mmarsale@cisco.com> | 2016-11-03 16:24:17 +0100 |
---|---|---|
committer | Maros Marsalek <mmarsale@cisco.com> | 2016-11-07 15:33:48 +0100 |
commit | 03a638b95da83e150d4f69451c8733b5f09c37aa (patch) | |
tree | 970f6f3afed85d073476b63108c7a12f1200557e /infra | |
parent | 5fd09f98a9fde773b275c2266f39552cda861713 (diff) |
HONEYCOMB-287 Infra micro-benchmarks
Config (write)
Operational (read)
Add -Pbenchmark to maven execution to include benchmarks
Change-Id: Ia4815ffc109e34629279b9418b962a9f91c38c30
Signed-off-by: Maros Marsalek <mmarsale@cisco.com>
Diffstat (limited to 'infra')
11 files changed, 1010 insertions, 0 deletions
diff --git a/infra/it/benchmark/asciidoc/Readme.adoc b/infra/it/benchmark/asciidoc/Readme.adoc new file mode 100644 index 000000000..44d2262ac --- /dev/null +++ b/infra/it/benchmark/asciidoc/Readme.adoc @@ -0,0 +1,3 @@ += benchmark + +Overview of benchmark
\ No newline at end of file diff --git a/infra/it/benchmark/pom.xml b/infra/it/benchmark/pom.xml new file mode 100644 index 000000000..5ee3c0881 --- /dev/null +++ b/infra/it/benchmark/pom.xml @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + ~ Copyright (c) 2016 Cisco and/or its affiliates. + ~ + ~ 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. + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <parent> + <groupId>io.fd.honeycomb.common</groupId> + <artifactId>honeycomb-parent</artifactId> + <version>1.16.12-SNAPSHOT</version> + <relativePath>../../../common/honeycomb-parent</relativePath> + </parent> + + <modelVersion>4.0.0</modelVersion> + <groupId>io.fd.honeycomb</groupId> + <artifactId>benchmark</artifactId> + <version>1.16.12-SNAPSHOT</version> + <packaging>jar</packaging> + + <properties> + <jmh.version>1.15</jmh.version> + <uberjar.name>benchmarks</uberjar.name> + </properties> + + <dependencies> + <dependency> + <groupId>org.openjdk.jmh</groupId> + <artifactId>jmh-core</artifactId> + <version>${jmh.version}</version> + </dependency> + <dependency> + <groupId>org.openjdk.jmh</groupId> + <artifactId>jmh-generator-annprocess</artifactId> + <version>${jmh.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>io.fd.honeycomb</groupId> + <artifactId>minimal-distribution</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>io.fd.honeycomb.it</groupId> + <artifactId>honeycomb-test-model</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + + <build> + <resources> + <resource> + <directory>${basedir}/src/main/resources</directory> + <!-- Enable maven to replace properties in resource files --> + <filtering>true</filtering> + </resource> + </resources> + <plugins> + <plugin> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>build-classpath</id> + <phase>compile</phase> + <goals> + <goal>build-classpath</goal> + </goals> + <configuration> + <outputProperty>depClasspath</outputProperty> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <executions> + <execution> + <id>benchmark</id> + <phase>test</phase> + <goals> + <goal>java</goal> + </goals> + <configuration> + <mainClass>org.openjdk.jmh.Main</mainClass> + <systemProperties> + <systemProperty> + <key>java.class.path</key> + <value>${project.build.outputDirectory}${path.separator}${depClasspath}</value> + </systemProperty> + </systemProperties> + <arguments> + <!-- Reference org.openjdk.jmh.runner.options.CommandLineOptions --> + + <!-- Fail on error --> + <argument>-foe=true</argument> + + <!-- PROFILING --> + <!-- cl: Classloader profiling via standard MBeans + comp: JIT compiler profiling via standard MBeans + gc: GC profiling via standard MBeans + hs_cl: HotSpot (tm) classloader profiling via implementation-specific MBeans + hs_comp: HotSpot (tm) JIT compiler profiling via implementation-specific MBeans + hs_gc: HotSpot (tm) memory manager (GC) profiling via implementation-specific MBeans + hs_rt: HotSpot (tm) runtime profiling via implementation-specific MBeans + hs_thr: HotSpot (tm) threading subsystem via implementation-specific MBeans + pauses: Pauses profiler + stack: Simple and naive Java stack profiler --> + <!--<argument>-prof=gc</argument>--> + + <!-- REPORT --> + <!-- formats = Available formats: text, csv, scsv, json, latex--> + <argument>-rf=csv</argument> + <argument>-rff=${project.build.directory}/benchmark.csv</argument> + </arguments> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> diff --git a/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data/DataBrokerConfigWriteBenchmark.java b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data/DataBrokerConfigWriteBenchmark.java new file mode 100644 index 000000000..145cd2bcb --- /dev/null +++ b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data/DataBrokerConfigWriteBenchmark.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * 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 io.fd.honeycomb.benchmark.data; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.name.Names; +import io.fd.honeycomb.benchmark.util.DataProvider; +import io.fd.honeycomb.benchmark.util.DataSubmitter; +import io.fd.honeycomb.benchmark.util.FileManager; +import io.fd.honeycomb.benchmark.util.NoopWriter; +import io.fd.honeycomb.infra.distro.cfgattrs.HoneycombConfiguration; +import io.fd.honeycomb.infra.distro.data.ConfigAndOperationalPipelineModule; +import io.fd.honeycomb.translate.write.WriterFactory; +import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedList; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +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.Timeout; +import org.openjdk.jmh.annotations.Warmup; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Measures the performance of CONFIG writes into BA DataBroker, backed by HC infrastructure and then NOOP writers. + */ +@Timeout(time = 20) +@Warmup(iterations = 2, time = 10) +@Measurement(iterations = 2, time = 10) +@Fork(2) +@State(Scope.Thread) +@BenchmarkMode(Mode.Throughput) +public class DataBrokerConfigWriteBenchmark extends AbstractModule implements FileManager { + + private static final Logger LOG = LoggerFactory.getLogger(DataBrokerConfigWriteBenchmark.class); + + @Param({"1", "10"/*, "100"*/}) + private int submitFrequency; + + @Param({"true", "false"}) + private boolean persistence; + + @Param({"put"/*, "merge"*/}) + private String operation; + private DataSubmitter submitter; + + @Param({"CONFIGURATION"}) + private LogicalDatastoreType dsType; + + @Param({DataProvider.SIMPLE_CONTAINER, DataProvider.LIST_IN_CONTAINER , DataProvider.COMPLEX_LIST_IN_CONTAINER}) + private String data; + private DataProvider dataProvider; + + /* + * TODO HONEYCOMB-288 Visualization notes: + * - visualize as 3 graphs, 1 for each data + * - each graph should show 4 lines. for the combinations of parameters: submitFrequency and persistence + * (if that's too much split or reduce submitFrequecy values that are shown in graph) + * + * TODO data need to be prepared for such visualization. Maybe if each benchmark class exposed a method to prepare + * that data from aggregated results... it might be easy + * (just maven exec plugin + main that invokes some method on all benchmark classes) + */ + + // Infra modules to load + private final Module[] modules = new Module[] { + new io.fd.honeycomb.infra.distro.schema.YangBindingProviderModule(), + new io.fd.honeycomb.infra.distro.schema.SchemaModule(), + new io.fd.honeycomb.infra.distro.data.ConfigAndOperationalPipelineModule(), + new io.fd.honeycomb.infra.distro.data.context.ContextPipelineModule(), + this}; + + private List<NoopWriter<?>> noopWriters = new ArrayList<>(); + private DataBroker dataBroker; + private long counter = 0; + private WriteTransaction tx; + private HoneycombConfiguration instance; + + @Setup(Level.Iteration) + public void setup() { + LOG.info("Setting up"); + submitter = DataSubmitter.from(operation); + dataProvider = DataProvider.from(data); + Injector injector = Guice.createInjector(modules); + final HoneycombConfiguration cfg = injector.getInstance(HoneycombConfiguration.class); + LOG.info("Configuration for Honeycomb: {}", cfg); + dataBroker = injector.getInstance(Key.get(DataBroker.class, + Names.named(ConfigAndOperationalPipelineModule.HONEYCOMB_CONFIG))); + } + + @TearDown(Level.Iteration) + public void tearDown() { + LOG.info("Tearing down after {} executions", counter); + counter = 0; + LOG.info("Writer invocations: {}", noopWriters); + noopWriters.clear(); + + tx = null; + dataBroker = null; + deleteFile(Paths.get(instance.peristConfigPath)); + deleteFile(Paths.get(instance.peristContextPath)); + } + + @Benchmark + public void write() { + // Count executions + counter++; + + // New transaction after it was committed + if (tx == null) { + tx = dataBroker.newWriteOnlyTransaction(); + } + + submitter.submit(dsType, tx, dataProvider.getId(counter), dataProvider.getData(counter)); + + // Commit based on frequency set + if (counter % submitFrequency == 0) { + try { + tx.submit().get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException("Submit failed", e); + } + tx = null; + } + } + + /** + * Inject custom modules e.g. configuration. + */ + @Override + protected void configure() { + try { + instance = getHoneycombConfiguration(persistence); + bind(HoneycombConfiguration.class).toInstance(instance); + } catch (IOException e) { + throw new RuntimeException("Unable to prepare configuration", e); + } + + final Multibinder<WriterFactory> writeBinder = Multibinder.newSetBinder(binder(), WriterFactory.class); + writeBinder.addBinding().toInstance(registry -> { + // Add noop writers for all data written in this benchmark + addWriter(registry, new NoopWriter<>(InstanceIdentifier.create(SimpleContainer.class))); + addWriter(registry, new NoopWriter<>(InstanceIdentifier.create(ContainerWithList.class))); + addWriter(registry, new NoopWriter<>(InstanceIdentifier.create(ContainerWithList.class) + .child(ListInContainer.class))); + addWriter(registry, new NoopWriter<>(InstanceIdentifier.create(ContainerWithList.class) + .child(ListInContainer.class) + .child(ContainerInList.class))); + addWriter(registry, new NoopWriter<>(InstanceIdentifier.create(ContainerWithList.class) + .child(ListInContainer.class) + .child(ContainerInList.class) + .child(NestedList.class))); + }); + } + + private void addWriter(final ModifiableWriterRegistryBuilder registry, final NoopWriter<?> handler) { + noopWriters.add(handler); + registry.add(handler); + } + + private static HoneycombConfiguration getHoneycombConfiguration(final boolean persistence) throws IOException { + final HoneycombConfiguration instance = new HoneycombConfiguration(); + instance.persistConfig = Optional.of(Boolean.toString(persistence)); + instance.persistContext = Optional.of(Boolean.toString(persistence)); + instance.peristConfigPath = FileManager.INSTANCE.createTempFile("config").toString(); + instance.peristContextPath = FileManager.INSTANCE.createTempFile("context").toString(); + return instance; + } +} diff --git a/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data/DataBrokerOperReadBenchmark.java b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data/DataBrokerOperReadBenchmark.java new file mode 100644 index 000000000..2fd9fa239 --- /dev/null +++ b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data/DataBrokerOperReadBenchmark.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * 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 io.fd.honeycomb.benchmark.data; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.name.Names; +import io.fd.honeycomb.benchmark.util.DataProvider; +import io.fd.honeycomb.benchmark.util.FileManager; +import io.fd.honeycomb.benchmark.util.StaticReader; +import io.fd.honeycomb.infra.distro.cfgattrs.HoneycombConfiguration; +import io.fd.honeycomb.infra.distro.data.ConfigAndOperationalPipelineModule; +import io.fd.honeycomb.translate.read.Reader; +import io.fd.honeycomb.translate.read.ReaderFactory; +import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +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.Timeout; +import org.openjdk.jmh.annotations.Warmup; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Measures the performance of CONFIG writes into BA DataBroker, backed by HC infrastructure and then NOOP writers. + */ +@Timeout(time = 20) +@Warmup(iterations = 2, time = 10) +@Measurement(iterations = 2, time = 10) +@Fork(2) +@State(Scope.Thread) +@BenchmarkMode(Mode.Throughput) +public class DataBrokerOperReadBenchmark extends AbstractModule implements FileManager { + + private static final Logger LOG = LoggerFactory.getLogger(DataBrokerOperReadBenchmark.class); + + @Param({"OPERATIONAL"}) + private LogicalDatastoreType dsType; + + // Persistence does not make a difference when only reading operational + @Param({"false"}) + private boolean persistence; + + @Param({DataProvider.SIMPLE_CONTAINER, DataProvider.LIST_IN_CONTAINER, DataProvider.COMPLEX_LIST_IN_CONTAINER}) + private String data; + private DataProvider dataProvider; + + /* + * TODO HONEYCOMB-288 Visualization notes: + * - visualize as 1 graph + * - just 3 lines + */ + + // Infra modules to load + private final Module[] modules = new Module[] { + new io.fd.honeycomb.infra.distro.schema.YangBindingProviderModule(), + new io.fd.honeycomb.infra.distro.schema.SchemaModule(), + new ConfigAndOperationalPipelineModule(), + new io.fd.honeycomb.infra.distro.data.context.ContextPipelineModule(), + this}; + + private List<Reader<?, ?>> noopReaders = new ArrayList<>(); + private DataBroker dataBroker; + private long counter = 0; + private ReadOnlyTransaction tx; + private HoneycombConfiguration instance; + + @Setup(Level.Iteration) + public void setup() { + LOG.info("Setting up"); + dataProvider = DataProvider.from(data); + Injector injector = Guice.createInjector(modules); + final HoneycombConfiguration cfg = injector.getInstance(HoneycombConfiguration.class); + LOG.info("Configuration for Honeycomb: {}", cfg); + dataBroker = injector.getInstance(Key.get(DataBroker.class, + Names.named(ConfigAndOperationalPipelineModule.HONEYCOMB_CONFIG))); + tx = dataBroker.newReadOnlyTransaction(); + } + + @TearDown(Level.Iteration) + public void tearDown() { + LOG.info("Tearing down after {} executions", counter); + counter = 0; + LOG.info("Reader invocations: {}", noopReaders); + noopReaders.clear(); + + tx.close(); + tx = null; + dataBroker = null; + deleteFile(Paths.get(instance.peristConfigPath)); + deleteFile(Paths.get(instance.peristContextPath)); + } + + @Benchmark + public void read() { + try { + tx.read(dsType, dataProvider.getId(counter++)).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException("Read failed", e); + } + } + + /** + * Inject custom modules e.g. configuration. + */ + @Override + protected void configure() { + try { + instance = getHoneycombConfiguration(persistence); + bind(HoneycombConfiguration.class).toInstance(instance); + } catch (IOException e) { + throw new RuntimeException("Unable to prepare configuration", e); + } + + final Multibinder<ReaderFactory> writeBinder = Multibinder.newSetBinder(binder(), ReaderFactory.class); + writeBinder.addBinding().toInstance(registry -> { + + switch (data) { + case DataProvider.SIMPLE_CONTAINER: { + addReader(registry, new StaticReader<>( + InstanceIdentifier.create(SimpleContainer.class), + DataProvider.from(DataProvider.SIMPLE_CONTAINER))); + break; + } + case DataProvider.LIST_IN_CONTAINER: { + registry.addStructuralReader( + InstanceIdentifier.create(ContainerWithList.class), ContainerWithListBuilder.class); + addReader(registry, new StaticReader<>( + InstanceIdentifier.create(ContainerWithList.class).child(ListInContainer.class), + DataProvider.from(DataProvider.LIST_IN_CONTAINER))); + break; + } + case DataProvider.COMPLEX_LIST_IN_CONTAINER: { + registry.addStructuralReader( + InstanceIdentifier.create(ContainerWithList.class), ContainerWithListBuilder.class); + addReader(registry, new StaticReader<>( + InstanceIdentifier.create(ContainerWithList.class).child(ListInContainer.class), + DataProvider.from(DataProvider.COMPLEX_LIST_IN_CONTAINER))); + break; + } + } + }); + } + + private void addReader(final ModifiableReaderRegistryBuilder registry, final Reader<?, ?> handler) { + noopReaders.add(handler); + registry.add(handler); + } + + private static HoneycombConfiguration getHoneycombConfiguration(final boolean persistence) throws IOException { + final HoneycombConfiguration instance = new HoneycombConfiguration(); + instance.persistConfig = Optional.of(Boolean.toString(persistence)); + instance.persistContext = Optional.of(Boolean.toString(persistence)); + instance.peristConfigPath = FileManager.INSTANCE.createTempFile("config").toString(); + instance.peristContextPath = FileManager.INSTANCE.createTempFile("context").toString(); + return instance; + } +} diff --git a/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/DataProvider.java b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/DataProvider.java new file mode 100644 index 000000000..7d3df2691 --- /dev/null +++ b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/DataProvider.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * 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 io.fd.honeycomb.benchmark.util; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.collect.Lists; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.ContainerWithList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.SimpleContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.ListInContainerKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.ContainerInListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hc.test.rev150105.container.with.list.list.in.container.container.in.list.NestedListBuilder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public interface DataProvider { + + String SIMPLE_CONTAINER = "simple-container"; + String LIST_IN_CONTAINER = "list-in-container"; + String COMPLEX_LIST_IN_CONTAINER = "complex-list-in-container"; + + InstanceIdentifier<?> getId(final long counter); + + DataObject getData(final long counter); + + static DataProvider from(String data) { + return new MapBackedDataProvider(data); + } + + final class MapBackedDataProvider implements DataProvider { + + private static final Map<String, DataProvider> map; + + static { + map = new HashMap<>(); + + final InstanceIdentifier<SimpleContainer> simpleContainerId = InstanceIdentifier.create(SimpleContainer.class); + map.put(SIMPLE_CONTAINER, new MultiValueDataProvider(Lists.newArrayList( + // Multiple values of container to ensure each time the value in DS after commit changes to trigger + // writers in test + new SingleValueDataProvider<>( + new SimpleContainerBuilder().setSimpleContainerName("first").build(), simpleContainerId), + new SingleValueDataProvider<>( + new SimpleContainerBuilder().setSimpleContainerName("second").build(), simpleContainerId), + new SingleValueDataProvider<>( + new SimpleContainerBuilder().setSimpleContainerName("third").build(), simpleContainerId)) + )); + map.put(LIST_IN_CONTAINER, new MultiValueDataProvider(getListInContainerValues(100_000))); + map.put(COMPLEX_LIST_IN_CONTAINER, + new MultiValueDataProvider(getComplexListInContainerValues(100_000))); + } + + private final DataProvider delegate; + + MapBackedDataProvider(final String data) { + checkArgument(map.containsKey(data)); + this.delegate = map.get(data); + } + + @Override + public InstanceIdentifier<?> getId(final long counter) { + return delegate.getId(counter); + } + + @Override + public DataObject getData(final long counter) { + return delegate.getData(counter); + } + + @Override + public String toString() { + return "MapBackedDataProvider{" + + "delegate=" + delegate + + '}'; + } + } + + + static List<DataProvider> getListInContainerValues(final int i) { + return IntStream.range(0, i) + .mapToObj(idx -> new SingleValueDataProvider<>( + new ListInContainerBuilder() + .setId((long) idx) + .build(), + InstanceIdentifier.create(ContainerWithList.class) + .child(ListInContainer.class, new ListInContainerKey((long) idx)) + )) + .collect(Collectors.toList()); + } + + static List<DataProvider> getComplexListInContainerValues(final int i) { + return IntStream.range(0, i) + .mapToObj(idx -> new SingleValueDataProvider<>( + new ListInContainerBuilder() + .setId((long) idx) + .setContainerInList(new ContainerInListBuilder() + .setName("nested container") + .setNestedList(Lists.newArrayList( + getNestedList("1"), + getNestedList("2"), + getNestedList("3"))) + .build()) + .build(), + InstanceIdentifier.create(ContainerWithList.class) + .child(ListInContainer.class, new ListInContainerKey((long) idx)) + )) + .collect(Collectors.toList()); + } + + static NestedList getNestedList(final String value) { + return new NestedListBuilder() + .setNestedId(value) + .setNestedName(value + "N") + .build(); + } + + final class SingleValueDataProvider<T extends DataObject> implements DataProvider { + + private final DataObject data; + private final InstanceIdentifier<?> id; + + SingleValueDataProvider(final T data, final InstanceIdentifier<T> id) { + this.data = data; + this.id = id; + } + + @Override + public InstanceIdentifier<?> getId(final long counter) { + return id; + } + + @Override + public DataObject getData(final long counter) { + return data; + } + + @Override + public String toString() { + return "SingleValueDataProvider{" + + "data=" + data + + ", id=" + id + + '}'; + } + } + + final class MultiValueDataProvider<T extends DataObject> implements DataProvider { + + private final List<DataProvider> values; + private int valueSize; + + public MultiValueDataProvider(final List<DataProvider> values) { + // Wrap as array list so that index lookup is fast + this.values = Lists.newArrayList(values); + this.valueSize = values.size(); + } + + @Override + public InstanceIdentifier<?> getId(final long counter) { + return values.get((int) (counter % valueSize)).getId(counter); + } + + @Override + public DataObject getData(final long counter) { + return values.get((int) (counter % valueSize)).getData(counter); + } + + @Override + public String toString() { + return "MultiValueDataProvider{" + + "valueSize=" + valueSize + + '}'; + } + } +} diff --git a/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/DataSubmitter.java b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/DataSubmitter.java new file mode 100644 index 000000000..ebad65335 --- /dev/null +++ b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/DataSubmitter.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * 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 io.fd.honeycomb.benchmark.util; + +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +@FunctionalInterface +public interface DataSubmitter { + + void submit(LogicalDatastoreType type, WriteTransaction tx, InstanceIdentifier<?> id, DataObject data); + + public static DataSubmitter from(String operation) { + switch (operation) { + case "put": { + return (type, tx, id, data) -> tx.put(type, (InstanceIdentifier) id, data); + } + case "merge": { + return (type, tx, id, data) -> tx.merge(type, (InstanceIdentifier) id, data); + } + default: throw new UnsupportedOperationException("Operation: " + operation); + } + } +} diff --git a/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/FileManager.java b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/FileManager.java new file mode 100644 index 000000000..8aa9f9370 --- /dev/null +++ b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/FileManager.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * 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 io.fd.honeycomb.benchmark.util; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +public interface FileManager { + + FileManager INSTANCE = new FileManager() {}; + + default Path createTempFile(String config) throws IOException { + return Files.createTempFile("hcbenchmark", config); + } + + default void deleteFile(final Path toDelete) { + if (toDelete != null) { + try { + Files.delete(toDelete); + } catch (IOException e) { + // NOOP, dont care about temp files too much + } + } + } + +} diff --git a/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/NoopWriter.java b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/NoopWriter.java new file mode 100644 index 000000000..f61bd1b5d --- /dev/null +++ b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/NoopWriter.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * 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 io.fd.honeycomb.benchmark.util; + +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.honeycomb.translate.write.Writer; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.concurrent.NotThreadSafe; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Noop writer suitable for lists as well. + */ +@NotThreadSafe +public final class NoopWriter<T extends DataObject> implements Writer<T> { + + private final InstanceIdentifier<T> id; + private long counter = 0; + + public NoopWriter(final InstanceIdentifier<T> id) { + this.id = id; + } + + @Override + public void update(@Nonnull final InstanceIdentifier<? extends DataObject> id, + @Nullable final DataObject dataBefore, + @Nullable final DataObject dataAfter, + @Nonnull final WriteContext ctx) throws WriteFailedException { + counter++; + // NOOP + } + + @Nonnull + @Override + public InstanceIdentifier<T> getManagedDataObjectType() { + return id; + } + + @Override + public String toString() { + return "NoopWriter{" + + id.getTargetType().getSimpleName() + + ", counter=" + counter + + '}'; + } +} diff --git a/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/StaticReader.java b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/StaticReader.java new file mode 100644 index 000000000..90e560152 --- /dev/null +++ b/infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/util/StaticReader.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * 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 io.fd.honeycomb.benchmark.util; + +import com.google.common.base.Optional; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.read.Reader; +import javax.annotation.Nonnull; +import javax.annotation.concurrent.NotThreadSafe; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Statically preconfigured reader. + */ +@NotThreadSafe +public final class StaticReader<T extends DataObject, B extends Builder<T>> implements Reader<T, B> { + + private final InstanceIdentifier<T> id; + private final DataProvider data; + private long counter = 0; + + public StaticReader(final InstanceIdentifier<T> id, final DataProvider data) { + this.id = id; + this.data = data; + } + + @Nonnull + @Override + public InstanceIdentifier<T> getManagedDataObjectType() { + return id; + } + + @Override + public String toString() { + return "NoopReader{" + + id.getTargetType().getSimpleName() + + ", counter=" + counter + + '}'; + } + + @Nonnull + @Override + public Optional<? extends DataObject> read(@Nonnull final InstanceIdentifier<? extends DataObject> id, + @Nonnull final ReadContext ctx) throws ReadFailedException { + counter++; + return Optional.of(data.getData(counter)); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<T> id, @Nonnull final B builder, + @Nonnull final ReadContext ctx) + throws ReadFailedException { + throw new UnsupportedOperationException("No read current attrs!"); + } + + @Nonnull + @Override + public B getBuilder(final InstanceIdentifier<T> id) { + throw new UnsupportedOperationException("No builder!"); + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, @Nonnull final T readValue) { + throw new UnsupportedOperationException("No merge!"); + } +} diff --git a/infra/it/benchmark/src/main/resources/logback.xml b/infra/it/benchmark/src/main/resources/logback.xml new file mode 100644 index 000000000..755300321 --- /dev/null +++ b/infra/it/benchmark/src/main/resources/logback.xml @@ -0,0 +1,26 @@ + <configuration scan="true"> + + <appender name="honeycomb.log" class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>${project.build.directory}/logs/honeycomb.log</file> + + <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> + <fileNamePattern>${project.build.directory}/logs/honeycomb.%d.log.zip</fileNamePattern> + <maxHistory>1</maxHistory> + </rollingPolicy> + + <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> + <maxFileSize>1MB</maxFileSize> + </triggeringPolicy> + + <encoder> + <pattern>%date{"yyyy-MM-dd HH:mm:ss.SSS z"} [%thread] %-5level %logger{35} - %msg%n</pattern> + </encoder> + </appender> + + <root level="warn"> + <appender-ref ref="honeycomb.log" /> + </root> + + <logger name="org.opendaylight" level="INFO"/> + <logger name="io.fd" level="INFO"/> +</configuration> diff --git a/infra/it/pom.xml b/infra/it/pom.xml index 5e1173413..3846955bc 100644 --- a/infra/it/pom.xml +++ b/infra/it/pom.xml @@ -33,6 +33,18 @@ <module>test-model</module> <module>it-test</module> </modules> + + + <!-- Activate from command line with mvn <goals> -Pbenchmark --> + <profiles> + <profile> + <id>benchmark</id> + <modules> + <module>benchmark</module> + </modules> + </profile> + </profiles> + <!-- DO NOT install or deploy the repo root pom as it's only needed to initiate a build --> <build> <plugins> |