/*******************************************************************************
* Copyright (c) 2016 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.tracecompass.analysis.timing.core.tests.store;
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Random;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.test.performance.Performance;
import org.eclipse.test.performance.PerformanceMeter;
import org.eclipse.tracecompass.internal.segmentstore.core.arraylist.ArrayListStore;
import org.eclipse.tracecompass.internal.segmentstore.core.arraylist.LazyArrayListStore;
import org.eclipse.tracecompass.internal.segmentstore.core.treemap.TreeMapStore;
import org.eclipse.tracecompass.segmentstore.core.BasicSegment;
import org.eclipse.tracecompass.segmentstore.core.ISegment;
import org.eclipse.tracecompass.segmentstore.core.ISegmentStore;
import org.eclipse.tracecompass.segmentstore.core.SegmentComparators;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* Segmentstore benchmarks, tests the performance for loads and reads.
*
* NOTE : Do not add this to isTracecompassFastYet, it is not information that
* is interesting for users, it is for developers.
*
* @category benchmark
*
* @author Matthew Khouzam
*
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RunWith(Parameterized.class)
public class SegmentStoreBenchmark {
private static final int DEFAULT_SAMPLE = 1000;
private static final int DEFAULT_LOOP_COUNT = 10;
private final ISegmentStore<@NonNull ISegment> fSegStore;
private final String fName;
private final Performance fPerf;
/**
* @return The arrays of parameters
*/
@Parameters(name = "{index}: {0}")
public static Iterable<Object[]> getParameters() {
return Arrays.asList(new Object[][] {
{ "Array list store", new ArrayListStore<>() },
{ "Lazy array list store", new LazyArrayListStore<>() },
{ "Treemap store", new TreeMapStore<>() },
});
}
/**
* Constructor
*
* @param name
* The name of this test
* @param segStore
* The segment store to fill for the benchmarks
*/
public SegmentStoreBenchmark(String name, ISegmentStore<@NonNull ISegment> segStore) {
fSegStore = segStore;
fName = name;
fPerf = Performance.getDefault();
}
/**
* Get the number of segments to add to the segment store
*
* @return The number of segments to add to the segment store
*/
protected long getSegmentStoreSize() {
return 1000000;
}
/**
* Add elements in order
*/
@Test
public void test1Ordered() {
PerformanceMeter pMorderedInsertion = fPerf.createPerformanceMeter("Ordered Insertion: " + fName);
int size = 1;
int[] fuzz = { 0 };
for (int i = 0; i < DEFAULT_LOOP_COUNT; i++) {
fSegStore.clear();
pMorderedInsertion.start();
populate(size, fuzz, fSegStore, 0, getSegmentStoreSize());
pMorderedInsertion.stop();
}
pMorderedInsertion.commit();
}
/**
* Add elements almost in order, this represents a typical degenerate use
* case. Then run all the iteration queries on the segment store.
*/
@Test
public void test2FuzzyOrder() {
int[] fuzz = fuzzyArray(DEFAULT_SAMPLE);
fullTest(DEFAULT_SAMPLE, fuzz, "Fuzzy");
}
/**
* Test adding elements in a random order, this is an atypical degenerate
* use case. Then run all the iteration queries on the segment store.
*/
@Test
public void test3Random() {
int[] fuzz = randomArray(DEFAULT_SAMPLE);
fullTest(DEFAULT_SAMPLE, fuzz, "Random");
}
/**
* Add elements almost in order, this represents a typical degenerate use
* case, and iterate while building then when done.
*/
@Test
public void test4FuzzyInsertIterTwice() {
int[] fuzz = fuzzyArray(DEFAULT_SAMPLE);
insertIterTwice(DEFAULT_SAMPLE, fuzz, "Fuzzy");
}
/**
* Test adding elements in a random order then iterate over the list then
* add more then iterate again, this is an atypical degenerate use case.
*/
@Test
public void test5RandomInsertIterTwice() {
int[] fuzz = randomArray(DEFAULT_SAMPLE);
insertIterTwice(DEFAULT_SAMPLE, fuzz, "Random");
}
private static int[] randomArray(int size) {
int[] fuzz = new int[DEFAULT_SAMPLE];
Random rng = new Random(10);
for (int i = 0; i < DEFAULT_SAMPLE; i++) {
fuzz[i] = Math.abs(rng.nextInt());
}
return fuzz;
}
private static int[] fuzzyArray(int size) {
int[] fuzz = new int[DEFAULT_SAMPLE];
Random rng = new Random(10);
for (int i = 0; i < DEFAULT_SAMPLE; i++) {
fuzz[i] = rng.nextInt(DEFAULT_SAMPLE);
}
return fuzz;
}
private void fullTest(int size, int[] fuzz, String distributionName) {
PerformanceMeter pMinsertion = fPerf.createPerformanceMeter(distributionName + " Insertion: " + fName);
PerformanceMeter pMiterateStart = fPerf.createPerformanceMeter(distributionName + " Iterate sorted by start: " + fName);
PerformanceMeter pMiterateEnd = fPerf.createPerformanceMeter(distributionName + " Iterate sorted by end: " + fName);
PerformanceMeter pMiterateDuration = fPerf.createPerformanceMeter(distributionName + " Iterate sorted by length: " + fName);
for (int i = 0; i < DEFAULT_LOOP_COUNT; i++) {
fSegStore.clear();
pMinsertion.start();
populate(size, fuzz, fSegStore, 0, getSegmentStoreSize());
pMinsertion.stop();
pMiterateStart.start();
int count = sortedIterate(fSegStore, SegmentComparators.INTERVAL_START_COMPARATOR);
pMiterateStart.stop();
assertEquals(fSegStore.size(), count);
pMiterateEnd.start();
count = sortedIterate(fSegStore, SegmentComparators.INTERVAL_END_COMPARATOR);
pMiterateEnd.stop();
assertEquals(fSegStore.size(), count);
pMiterateDuration.start();
count = sortedIterate(fSegStore, SegmentComparators.INTERVAL_LENGTH_COMPARATOR);
pMiterateDuration.stop();
assertEquals(fSegStore.size(), count);
}
pMinsertion.commit();
pMiterateStart.commit();
pMiterateEnd.commit();
pMiterateDuration.commit();
}
private void insertIterTwice(int size, int[] fuzz, String distributionName) {
PerformanceMeter pMinsertion1 = fPerf.createPerformanceMeter(distributionName + " First Insertion: " + fName);
PerformanceMeter pMiterate1 = fPerf.createPerformanceMeter(distributionName + " First Iteration: " + fName);
PerformanceMeter pMinsertion2 = fPerf.createPerformanceMeter(distributionName + " Second Insertion: " + fName);
PerformanceMeter pMiterate2 = fPerf.createPerformanceMeter(distributionName + " Second Iteration: " + fName);
for (int i = 0; i < DEFAULT_LOOP_COUNT; i++) {
fSegStore.clear();
pMinsertion1.start();
populate(size, fuzz, fSegStore, 0, getSegmentStoreSize() / 2);
pMinsertion1.stop();
pMiterate1.start();
int count = iterate(fSegStore);
pMiterate1.stop();
assertEquals(fSegStore.size(), count);
pMinsertion2.start();
populate(size, fuzz, fSegStore, getSegmentStoreSize() / 2 + 1, getSegmentStoreSize());
pMinsertion2.stop();
pMiterate2.start();
count = iterate(fSegStore);
pMiterate2.stop();
assertEquals(fSegStore.size(), count);
}
pMinsertion1.commit();
pMiterate1.commit();
pMinsertion2.commit();
pMiterate2.commit();
}
private static int iterate(Iterable<@NonNull ISegment> store) {
int count = 0;
Iterator<@NonNull ISegment> iterator = store.iterator();
while (iterator.hasNext()) {
count++;
iterator.next();
}
return count;
}
private static int sortedIterate(ISegmentStore<@NonNull ISegment> store, @NonNull Comparator<@NonNull ISegment> order) {
Iterable<@NonNull ISegment> iterable = store.iterator(order);
return iterate(iterable);
}
private static void populate(int size, int[] fuzz, ISegmentStore<@NonNull ISegment> store, long low, long high) {
for (long i = low; i < high; i++) {
long start = i + fuzz[(int) (i % size)];
store.add(new BasicSegment(start, start + 10));
}
}
}