package com.ldbc.driver.runtime.coordination; import com.google.common.collect.Lists; import com.ldbc.driver.generator.GeneratorFactory; import com.ldbc.driver.generator.RandomDataGeneratorFactory; import com.ldbc.driver.temporal.SystemTimeSource; import com.ldbc.driver.temporal.TimeSource; import org.junit.Ignore; import org.junit.Test; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertThat; public class LocalCompletedTimeTrackerImplTest { /* ------- PERFORMANCE ------- */ @Ignore @Test public void comparePerformanceOfImplementations() throws CompletionTimeException { int timesCountBigger = 1000000; int timesCountSmaller = 10000; int benchmarkRepetitions = 10; long totalDuration_TreeMultiset_AddedRemovedImmediatelyAsMilli = 0l; long totalDuration_List_AddedRemovedImmediatelyAsMilli = 0l; long totalDuration_TreeMultiset_AddAllThenRemoveAllSequentiallyAsMilli = 0l; long totalDuration_List_AddAllThenRemoveAllSequentiallyAsMilli = 0l; long totalDuration_TreeMultiSet_JustAddAsMilli = 0l; long totalDuration_List_JustAddAsMilli = 0l; long totalDuration_TreeMultiset_AddAllThenRemoveAllRandomlyAsMilli = 0l; long totalDuration_List_AddAllThenRemoveAllRandomlyAsMilli = 0l; for (int i = 0; i < benchmarkRepetitions; i++) { // ****** // Added Removed Immediately // ****** totalDuration_TreeMultiset_AddedRemovedImmediatelyAsMilli = totalDuration_TreeMultiset_AddedRemovedImmediatelyAsMilli + benchmarkTimesAddedAndRemovedImmediatelyAsMilli( LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl.createUsingTreeMultiSet(), timesCountBigger ) ; totalDuration_List_AddedRemovedImmediatelyAsMilli = totalDuration_List_AddedRemovedImmediatelyAsMilli + benchmarkTimesAddedAndRemovedImmediatelyAsMilli( LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl.createUsingArrayList(), timesCountBigger ); // ****** // Add All Then Remove All Sequentially // ****** totalDuration_TreeMultiset_AddAllThenRemoveAllSequentiallyAsMilli = totalDuration_TreeMultiset_AddAllThenRemoveAllSequentiallyAsMilli + benchmarkTimesAllAddedThenAllRemovedSequentiallyAsMilli( LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl.createUsingTreeMultiSet(), timesCountSmaller ); totalDuration_List_AddAllThenRemoveAllSequentiallyAsMilli = totalDuration_List_AddAllThenRemoveAllSequentiallyAsMilli + benchmarkTimesAllAddedThenAllRemovedSequentiallyAsMilli( LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl.createUsingArrayList(), timesCountSmaller ); // ****** // Just Add // ****** totalDuration_TreeMultiSet_JustAddAsMilli = totalDuration_TreeMultiSet_JustAddAsMilli + benchmarkTimesJustAddedAsMilli( LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl.createUsingTreeMultiSet(), timesCountBigger ); totalDuration_List_JustAddAsMilli = totalDuration_List_JustAddAsMilli + benchmarkTimesJustAddedAsMilli( LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl.createUsingArrayList(), timesCountBigger ); // ****** // Add All Then Remove All Randomly // ****** totalDuration_TreeMultiset_AddAllThenRemoveAllRandomlyAsMilli = totalDuration_TreeMultiset_AddAllThenRemoveAllRandomlyAsMilli + benchmarkTimesAllAddedThenAllRemovedRandomlyAsMilli( LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl.createUsingTreeMultiSet(), timesCountSmaller ); totalDuration_List_AddAllThenRemoveAllRandomlyAsMilli = totalDuration_List_AddAllThenRemoveAllRandomlyAsMilli + benchmarkTimesAllAddedThenAllRemovedRandomlyAsMilli( LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl.createUsingArrayList(), timesCountSmaller ); } System.out.println("TreeMultiSet (add " + timesCountBigger + "):\t\t\t\t\t\t\t\t\t" + totalDuration_TreeMultiSet_JustAddAsMilli + " <- add to large/growing"); System.out.println("List (add " + timesCountBigger + "):\t\t\t\t\t\t\t\t\t\t\t" + totalDuration_List_JustAddAsMilli + " <- add to large/growing"); System.out.println(); System.out.println("TreeMultiSet (add one/remove one x " + timesCountBigger + "):\t\t\t\t" + totalDuration_TreeMultiset_AddedRemovedImmediatelyAsMilli + " <- add to small`,remove from small sequentially"); System.out.println("List (add one/remove one):\t\t\t\t\t\t\t\t\t" + totalDuration_List_AddedRemovedImmediatelyAsMilli + " <- add to small,remove from small sequentially"); System.out.println(); System.out.println("TreeMultiSet (add " + timesCountSmaller + "/remove " + timesCountSmaller + " sequentially):\t\t\t" + totalDuration_TreeMultiset_AddAllThenRemoveAllSequentiallyAsMilli + " <- add to large/growing, removal from large/growing sequentially"); System.out.println("List (add " + timesCountSmaller + "/remove " + timesCountSmaller + " sequentially):\t\t\t\t\t" + totalDuration_List_AddAllThenRemoveAllSequentiallyAsMilli + " <- add to large/growing, removal from large/growing sequentially"); System.out.println(); System.out.println("TreeMultiSet (add " + timesCountSmaller + "/remove " + timesCountSmaller + " randomly):\t\t\t\t" + totalDuration_TreeMultiset_AddAllThenRemoveAllRandomlyAsMilli + " <- add to large/growing, removal from large/growing randomly"); System.out.println("List (add " + timesCountSmaller + "/remove " + timesCountSmaller + " randomly):\t\t\t\t\t\t" + totalDuration_List_AddAllThenRemoveAllRandomlyAsMilli + " <- add to large/growing, removal from large/growing randomly"); } public long benchmarkTimesAddedAndRemovedImmediatelyAsMilli( LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl tracker, int timesCount) throws CompletionTimeException { TimeSource timeSource = new SystemTimeSource(); GeneratorFactory gf = new GeneratorFactory(new RandomDataGeneratorFactory(42L)); Iterator<Long> times = gf.limit( gf.incrementing(0l, 1l), timesCount ); long startTimeAsMilli = timeSource.nowAsMilli(); while (times.hasNext()) { long timeAsMilli = times.next(); tracker.addCompletedTimeAsMilli(timeAsMilli); tracker.removeTimesLowerThanAndReturnHighestRemoved(timeAsMilli); } long finishTimeAsMilli = timeSource.nowAsMilli(); return finishTimeAsMilli - startTimeAsMilli; } public long benchmarkTimesAllAddedThenAllRemovedSequentiallyAsMilli( LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl tracker, int timesCount) throws CompletionTimeException { TimeSource timeSource = new SystemTimeSource(); GeneratorFactory gf = new GeneratorFactory(new RandomDataGeneratorFactory(42L)); List<Long> timesAsMilli = Lists.newArrayList( gf.limit( gf.incrementing(0l, 1l), timesCount ) ); long startTimeAsMilli = timeSource.nowAsMilli(); // add all times for (long timeAsMilli : timesAsMilli) { tracker.addCompletedTimeAsMilli(timeAsMilli); } // remove all times for (long timeAsMilli : timesAsMilli) { tracker.removeTimesLowerThanAndReturnHighestRemoved(timeAsMilli); } long finishTimeAsMilli = timeSource.nowAsMilli(); return finishTimeAsMilli - startTimeAsMilli; } public long benchmarkTimesAllAddedThenAllRemovedRandomlyAsMilli( LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl tracker, int timesCount) throws CompletionTimeException { TimeSource timeSource = new SystemTimeSource(); GeneratorFactory gf = new GeneratorFactory(new RandomDataGeneratorFactory(42L)); final List<Long> times = Lists.newArrayList( gf.limit( gf.incrementing(0l, 1l), timesCount) ); // create iterator to add times from, in sequential order <-- requirement of tracker Iterator<Long> timesToAdd = Lists.newArrayList(times).iterator(); // create iterator to remove times from, in random order List<Long> timesToRemoveList = new ArrayList<>(); Iterator<Double> uniforms = gf.uniform(0.0, 1.0); while (false == times.isEmpty()) { int index = (int) Math.round(Math.floor(uniforms.next() * times.size())); long timeToRemoveAsMilli = times.remove(index); assertThat(timeToRemoveAsMilli, is(notNullValue())); timesToRemoveList.add(timeToRemoveAsMilli); } assertThat(timesToRemoveList.size(), is(timesCount)); Iterator<Long> timesToRemove = timesToRemoveList.iterator(); long startTimeAsMilli = timeSource.nowAsMilli(); // add all times while (timesToAdd.hasNext()) { long timeAsMilli = timesToAdd.next(); tracker.addCompletedTimeAsMilli(timeAsMilli); } // remove all times while (timesToRemove.hasNext()) { long timeAsMilli = timesToRemove.next(); tracker.removeTimesLowerThanAndReturnHighestRemoved(timeAsMilli); } long finishTimeAsMilli = timeSource.nowAsMilli(); return finishTimeAsMilli - startTimeAsMilli; } public long benchmarkTimesJustAddedAsMilli( LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl tracker, int timesCount) throws CompletionTimeException { TimeSource timeSource = new SystemTimeSource(); GeneratorFactory gf = new GeneratorFactory(new RandomDataGeneratorFactory(42L)); List<Long> times = Lists.newArrayList( gf.limit( gf.incrementing(0l, 1l), timesCount) ); long startTimeAsMilli = timeSource.nowAsMilli(); // add all times for (long timeAsMilli : times) { tracker.addCompletedTimeAsMilli(timeAsMilli); } long finishTimeAsMilli = timeSource.nowAsMilli(); return finishTimeAsMilli - startTimeAsMilli; } /* ------- CORRECTNESS ------- */ @Test public void shouldReturnNullsWhenNoTimesHaveBeenSubmitted() throws CompletionTimeException { // Given LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl tracker = LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl.createUsingTreeMultiSet(); // When // nothing // Then assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(Long.MAX_VALUE), is(-1l)); } @Test public void shouldRemoveTimesCorrectlyWhenThereIsOnlyOneTime_UsingTreeMultiSet() throws CompletionTimeException { shouldRemoveTimesCorrectlyWhenThereIsOnlyOneTime(LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl.createUsingTreeMultiSet()); } @Test public void shouldRemoveTimesCorrectlyWhenThereIsOnlyOneTime_UsingList() throws CompletionTimeException { shouldRemoveTimesCorrectlyWhenThereIsOnlyOneTime(LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl.createUsingArrayList()); } public void shouldRemoveTimesCorrectlyWhenThereIsOnlyOneTime(LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl tracker) throws CompletionTimeException { // Given // tracker // When tracker.addCompletedTimeAsMilli(1l); // Then assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(0l), is(-1l)); assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(1l), is(-1l)); assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(Long.MAX_VALUE), is(1l)); } @Test public void shouldRemoveTimesCorrectlyWhenThereIsAreMultipleTimesThatAreAddedInOrder_UsingTreeMultiSet() throws CompletionTimeException { shouldRemoveTimesCorrectlyWhenThereIsAreMultipleTimesThatAreAddedInOrder(LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl.createUsingTreeMultiSet()); } @Test public void shouldRemoveTimesCorrectlyWhenThereIsAreMultipleTimesThatAreAddedInOrder_UsingList() throws CompletionTimeException { shouldRemoveTimesCorrectlyWhenThereIsAreMultipleTimesThatAreAddedInOrder(LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl.createUsingArrayList()); } public void shouldRemoveTimesCorrectlyWhenThereIsAreMultipleTimesThatAreAddedInOrder(LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl tracker) throws CompletionTimeException { // Given // tracker // When/Then assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(0l), is(-1l)); assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(Long.MAX_VALUE), is(-1l)); // [1] tracker.addCompletedTimeAsMilli(1l); // [1] assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(0l), is(-1l)); assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(1l), is(-1l)); // [1,2] tracker.addCompletedTimeAsMilli(2l); // [1,2] assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(0l), is(-1l)); assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(1l), is(-1l)); // [1,2,3] tracker.addCompletedTimeAsMilli(3l); // [ ,2,3] assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(2l), is(1l)); // [ ,2,3,4] tracker.addCompletedTimeAsMilli(4l); // [ ,2,3,4,5,6,7] tracker.addCompletedTimeAsMilli(5l); tracker.addCompletedTimeAsMilli(6l); tracker.addCompletedTimeAsMilli(7l); // [ , , , ,5,6,7] assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(5l), is(4l)); // [ , , , ,5,6,7,8,9,10] tracker.addCompletedTimeAsMilli(8l); tracker.addCompletedTimeAsMilli(9l); tracker.addCompletedTimeAsMilli(10l); // [ , , , ,5,6,7,8,9,10,11,14,15] tracker.addCompletedTimeAsMilli(11l); tracker.addCompletedTimeAsMilli(14l); tracker.addCompletedTimeAsMilli(15l); // [ , , , , , , , , , , ,14,15] assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(13l), is(11l)); assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(Long.MAX_VALUE), is(15l)); assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(Long.MAX_VALUE), is(-1l)); } @Test public void shouldRemoveTimesCorrectlyWhenThereIsAreMultipleTimesThatAreAddedOutOfOrder_UsingTreeMultiSet() throws CompletionTimeException { shouldRemoveTimesCorrectlyWhenThereIsAreMultipleTimesThatAreAddedOutOfOrder(LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl.createUsingTreeMultiSet()); } @Test public void shouldRemoveTimesCorrectlyWhenThereIsAreMultipleTimesThatAreAddedOutOfOrder_UsingList() throws CompletionTimeException { shouldRemoveTimesCorrectlyWhenThereIsAreMultipleTimesThatAreAddedOutOfOrder(LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl.createUsingArrayList()); } public void shouldRemoveTimesCorrectlyWhenThereIsAreMultipleTimesThatAreAddedOutOfOrder(LocalCompletionTimeStateManager.LocalCompletedTimeTrackerImpl tracker) throws CompletionTimeException { // Given // tracker // When/Then assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(0l), is(-1l)); assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(Long.MAX_VALUE), is(-1l)); // [1] tracker.addCompletedTimeAsMilli(1l); // [0,0,1,1] tracker.addCompletedTimeAsMilli(0l); tracker.addCompletedTimeAsMilli(0l); tracker.addCompletedTimeAsMilli(1l); // [0,0,1,1,9,2,6] tracker.addCompletedTimeAsMilli(9l); tracker.addCompletedTimeAsMilli(2l); tracker.addCompletedTimeAsMilli(6l); // [ , , , ,9, ,6] assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(4l), is(2l)); // [ , , , ,9, ,6,1,0,0,4] tracker.addCompletedTimeAsMilli(1l); tracker.addCompletedTimeAsMilli(0l); tracker.addCompletedTimeAsMilli(0l); tracker.addCompletedTimeAsMilli(4l); // [ , , , ,9, ,6, , , ,4] assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(4l), is(1l)); // [ , , , ,9, ,6, , , ,4,1,2,3,4,5,6,7,8,9] tracker.addCompletedTimeAsMilli(1l); tracker.addCompletedTimeAsMilli(2l); tracker.addCompletedTimeAsMilli(3l); tracker.addCompletedTimeAsMilli(4l); tracker.addCompletedTimeAsMilli(5l); tracker.addCompletedTimeAsMilli(6l); tracker.addCompletedTimeAsMilli(7l); tracker.addCompletedTimeAsMilli(8l); tracker.addCompletedTimeAsMilli(9l); // [ , , , ,9, ,6, , , , , , , , , ,6,7,8,9] assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(6l), is(5l)); // [ , , , ,9, ,6, , , , , , , , , ,6,7,8,9,10] tracker.addCompletedTimeAsMilli(10l); // [ , , , ,9, ,6, , , , , , , , , ,6,7,8,9,10] assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(6l), is(-1l)); // [ , , , ,9, , , , , , , , , , , , , ,8,9,10] assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(8l), is(7l)); // [ , , , ,9, , , , , , , , , , , , , ,8,9,10,0] tracker.addCompletedTimeAsMilli(0l); // [ , , , ,9, , , , , , , , , , , , , ,8,9,10,0,15] tracker.addCompletedTimeAsMilli(15l); // [ , , , ,9, , , , , , , , , , , , , ,8,9,10, ,15] assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(6l), is(0l)); // [ , , , , , , , , , , , , , , , , , , , , , ,15] assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(12l), is(10l)); // [ , , , , , , , , , , , , , , , , , , , , , ,15] assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(15l), is(-1l)); // [ , , , , , , , , , , , , , , , , , , , , , , ] assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(16l), is(15l)); // [ , , , , , , , , , , , , , , , , , , , , , , ] assertThat(tracker.removeTimesLowerThanAndReturnHighestRemoved(Long.MAX_VALUE), is(-1l)); } }