package com.ldbc.driver.statistics; import com.ldbc.driver.ChildOperationGenerator; import com.ldbc.driver.Operation; import com.ldbc.driver.WorkloadException; import com.ldbc.driver.WorkloadStreams; import com.ldbc.driver.generator.GeneratorFactory; import com.ldbc.driver.generator.RandomDataGeneratorFactory; import com.ldbc.driver.runtime.metrics.ContinuousMetricManager; import com.ldbc.driver.runtime.metrics.MetricsCollectionException; import com.ldbc.driver.util.Bucket; import com.ldbc.driver.util.Histogram; import java.util.*; public class WorkloadStatisticsCalculator { /** * TODO report how frequently GCT is updated */ public WorkloadStatistics calculate(WorkloadStreams workloadStreams, long maxExpectedInterleaveAsMilli) throws MetricsCollectionException { Histogram<Class, Long> operationMixHistogram = new Histogram<>(0l); GeneratorFactory gf = new GeneratorFactory(new RandomDataGeneratorFactory(42l)); ContinuousMetricManager operationInterleaves = new ContinuousMetricManager(null, null, maxExpectedInterleaveAsMilli, 5); long previousOperationStartTimeAsMilli = -1; final Map<Class, Long> previousOperationStartTimesAsMilliByOperationType = new HashMap<>(); Map<Class, ContinuousMetricManager> operationInterleavesByOperationType = new HashMap<>(); Map<Class, Long> firstStartTimesAsMilliByOperationType = new HashMap<>(); Map<Class, Long> lastStartTimesAsMilliByOperationType = new HashMap<>(); final Set<Class> dependencyOperationTypes = new HashSet<>(); final Set<Class> dependentOperationTypes = new HashSet<>(); // If there are no operations in the stream (e.g. they are all disabled) there is no point tracking the depend operations if (workloadStreams.asynchronousStream().dependencyOperations().hasNext() || workloadStreams.asynchronousStream().nonDependencyOperations().hasNext()) { dependentOperationTypes.addAll(workloadStreams.asynchronousStream().dependentOperationTypes()); dependencyOperationTypes.addAll(workloadStreams.asynchronousStream().dependencyOperationTypes()); } for (WorkloadStreams.WorkloadStreamDefinition streamDefinition : workloadStreams.blockingStreamDefinitions()) { // If there are no operations in the stream (e.g. they are all disabled) there is no point tracking the depend operations if (streamDefinition.dependencyOperations().hasNext() || streamDefinition.nonDependencyOperations().hasNext()) { dependentOperationTypes.addAll(streamDefinition.dependentOperationTypes()); dependencyOperationTypes.addAll(streamDefinition.dependencyOperationTypes()); } } List<Iterator<Operation>> operationIterators = new ArrayList<>(); operationIterators.add( new StreamWithChildOperationGenerator(workloadStreams.asynchronousStream().dependencyOperations(), workloadStreams.asynchronousStream().childOperationGenerator()) ); operationIterators.add( new StreamWithChildOperationGenerator(workloadStreams.asynchronousStream().nonDependencyOperations(), workloadStreams.asynchronousStream().childOperationGenerator()) ); for (WorkloadStreams.WorkloadStreamDefinition blockingStreamDefinition : workloadStreams.blockingStreamDefinitions()) { operationIterators.add( new StreamWithChildOperationGenerator(blockingStreamDefinition.dependencyOperations(), blockingStreamDefinition.childOperationGenerator()) ); operationIterators.add( new StreamWithChildOperationGenerator(blockingStreamDefinition.nonDependencyOperations(), blockingStreamDefinition.childOperationGenerator()) ); } Iterator<Operation> operations = gf.mergeSortOperationsByScheduledStartTime( operationIterators.toArray(new Iterator[operationIterators.size()]) ); Map<Class, Long> lowestDependencyDurationAsMilliByOperationType = new HashMap<>(); while (operations.hasNext()) { Operation operation = operations.next(); Class operationType = operation.getClass(); long operationStartTimeAsMilli = operation.scheduledStartTimeAsMilli(); long operationDependencyTimeAsMilli = operation.dependencyTimeStamp(); long operationDependencyDurationAsMilli = operationStartTimeAsMilli - operationDependencyTimeAsMilli; // Operation Mix operationMixHistogram.incOrCreateBucket(Bucket.DiscreteBucket.create(operationType), 1l); // Interleaves if (-1 != previousOperationStartTimeAsMilli) { long interleaveDurationAsMilli = operationStartTimeAsMilli - previousOperationStartTimeAsMilli; operationInterleaves.addMeasurement(interleaveDurationAsMilli); } previousOperationStartTimeAsMilli = operationStartTimeAsMilli; // Interleaves by operation type ContinuousMetricManager operationInterleaveForOperationType = operationInterleavesByOperationType.get(operationType); if (null == operationInterleaveForOperationType) { operationInterleaveForOperationType = new ContinuousMetricManager(null, null, maxExpectedInterleaveAsMilli, 5); operationInterleavesByOperationType.put(operationType, operationInterleaveForOperationType); } Long previousOperationStartTimeAsMilliForOperationType = previousOperationStartTimesAsMilliByOperationType.get(operationType); if (null != previousOperationStartTimeAsMilliForOperationType) { long interleaveDurationAsMilli = operationStartTimeAsMilli - previousOperationStartTimeAsMilliForOperationType; operationInterleaveForOperationType.addMeasurement(interleaveDurationAsMilli); } previousOperationStartTimesAsMilliByOperationType.put(operationType, operationStartTimeAsMilli); // Dependency duration by operation type long lowestDependencyDurationAsMilliForOperationType = (lowestDependencyDurationAsMilliByOperationType.containsKey(operationType)) ? lowestDependencyDurationAsMilliByOperationType.get(operationType) : Long.MAX_VALUE; if (operationDependencyDurationAsMilli < lowestDependencyDurationAsMilliForOperationType) lowestDependencyDurationAsMilliByOperationType.put(operationType, operationDependencyDurationAsMilli); // First start times by operation type if (false == firstStartTimesAsMilliByOperationType.containsKey(operationType)) firstStartTimesAsMilliByOperationType.put(operationType, operationStartTimeAsMilli); // Last start times by operation type lastStartTimesAsMilliByOperationType.put(operationType, operationStartTimeAsMilli); } return new WorkloadStatistics( firstStartTimesAsMilliByOperationType, lastStartTimesAsMilliByOperationType, operationMixHistogram, operationInterleaves, operationInterleavesByOperationType, dependencyOperationTypes, dependentOperationTypes, lowestDependencyDurationAsMilliByOperationType); } private static class StreamWithChildOperationGenerator implements Iterator<Operation> { private static final Object RESULT = null; private final Iterator<Operation> stream; private final ChildOperationGenerator childOperationGenerator; private double childOperationGeneratorState; private Operation nextChildOperation; private StreamWithChildOperationGenerator(Iterator<Operation> stream, ChildOperationGenerator childOperationGenerator) { this.stream = stream; this.childOperationGenerator = childOperationGenerator; if (null != this.childOperationGenerator) { this.childOperationGeneratorState = this.childOperationGenerator.initialState(); } this.nextChildOperation = null; } @Override public boolean hasNext() { return null != nextChildOperation || stream.hasNext(); } @Override public Operation next() { Operation next = (null != nextChildOperation) ? nextChildOperation : stream.next(); if (null != this.childOperationGenerator) { try { nextChildOperation = childOperationGenerator.nextOperation(childOperationGeneratorState, next, RESULT, next.scheduledStartTimeAsMilli(), 0l); } catch (WorkloadException e) { throw new RuntimeException("Error encountered while retrieving next child operation", e); } childOperationGeneratorState = (null == nextChildOperation) ? childOperationGenerator.initialState() : childOperationGenerator.updateState(childOperationGeneratorState, next.type()); } return next; } @Override public void remove() { throw new UnsupportedOperationException(); } } }