From 03a638b95da83e150d4f69451c8733b5f09c37aa Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Thu, 3 Nov 2016 16:24:17 +0100 Subject: 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 --- .../data/DataBrokerConfigWriteBenchmark.java | 210 +++++++++++++++++++++ .../data/DataBrokerOperReadBenchmark.java | 198 +++++++++++++++++++ 2 files changed, 408 insertions(+) create mode 100644 infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data/DataBrokerConfigWriteBenchmark.java create mode 100644 infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data/DataBrokerOperReadBenchmark.java (limited to 'infra/it/benchmark/src/main/java/io/fd/honeycomb/benchmark/data') 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> 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 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> 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 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; + } +} -- cgit 1.2.3-korg