-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add reordering optimizations, fix #35
- Loading branch information
Showing
8 changed files
with
806 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
129 changes: 129 additions & 0 deletions
129
...pastreamer/interopoptimizer/standard/internal/strategy/MoveAnonymousLambdaOperations.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
package com.speedment.jpastreamer.interopoptimizer.standard.internal.strategy; | ||
|
||
import com.speedment.jpastreamer.field.Field; | ||
import com.speedment.jpastreamer.field.predicate.SpeedmentPredicate; | ||
import com.speedment.jpastreamer.interopoptimizer.IntermediateOperationOptimizer; | ||
import com.speedment.jpastreamer.pipeline.Pipeline; | ||
import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperation; | ||
import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperationType; | ||
|
||
import java.util.LinkedList; | ||
|
||
import static com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperationType.*; | ||
|
||
public class MoveAnonymousLambdaOperations implements IntermediateOperationOptimizer { | ||
|
||
@Override | ||
public <T> Pipeline<T> optimize(Pipeline<T> pipeline) { | ||
|
||
LinkedList<IntermediateOperation<?, ?>> intermediateOperations = pipeline.intermediateOperations(); | ||
LinkedList<IntermediateOperation<?, ?>> optimizedOperations = new LinkedList<>(); | ||
for (int i = 0; i < intermediateOperations.size(); i++) { | ||
final IntermediateOperation<?, ?> intermediateOperation = intermediateOperations.get(i); | ||
if (i == 0 || !movable(intermediateOperation.type())) { | ||
optimizedOperations.add(intermediateOperation); | ||
continue; | ||
} | ||
int position = optimizedOperations.size(); // default - end of list | ||
for(int j = optimizedOperations.size() - 1; j >= 0; j--) { | ||
final IntermediateOperation<?, ?> currentOperation = optimizedOperations.get((j)); | ||
if(swappable(intermediateOperation.type(), currentOperation.type()) && anonymousLambda(currentOperation)) { | ||
position = j; | ||
} else { | ||
break; | ||
} | ||
} | ||
if (position < optimizedOperations.size()) { | ||
optimizedOperations.add(position, intermediateOperation); | ||
} else { | ||
optimizedOperations.add(intermediateOperation); | ||
} | ||
} | ||
|
||
// Update pipeline | ||
for (int k = 0; k < intermediateOperations.size(); k++) { | ||
final IntermediateOperation<?, ?> intermediateOperation = optimizedOperations.get(k); | ||
intermediateOperations.set(k, intermediateOperation); | ||
} | ||
|
||
return pipeline; | ||
} | ||
|
||
public <T> Pipeline<T> optimize2(Pipeline<T> pipeline) { | ||
|
||
LinkedList<IntermediateOperation<?, ?>> intermediateOperations = pipeline.intermediateOperations(); | ||
|
||
int i = 0; | ||
while(i < intermediateOperations.size() - 1) { | ||
final IntermediateOperation<?, ?> intermediateOperation = intermediateOperations.get(i); | ||
final IntermediateOperationType iot = intermediateOperation.type(); | ||
if (movable(iot) && !anonymousLambda(intermediateOperation) ) { | ||
// We only move movable operations with anonymous lambdas | ||
int j = i + 1; | ||
int currentPos = i; | ||
while (j < intermediateOperations.size()) { | ||
final IntermediateOperation<?, ?> next = intermediateOperations.get(j); | ||
final IntermediateOperationType iotNext = next.type(); | ||
if (swappable(iot, iotNext)) { // check if current lambda can be swapped with next operation | ||
if (iotNext == DISTINCT || anonymousLambda(next)) { | ||
i = -1; | ||
currentPos = swapOperations(intermediateOperations, currentPos, j); | ||
} else { | ||
j++; | ||
i++; | ||
continue; | ||
} | ||
} else { | ||
break; | ||
} | ||
j++; | ||
} | ||
} | ||
i++; | ||
} | ||
|
||
return pipeline; | ||
} | ||
|
||
|
||
private boolean movable(final IntermediateOperationType type) { | ||
return type == FILTER || type == SORTED || type == DISTINCT; | ||
} | ||
|
||
private boolean swappable(final IntermediateOperationType type, final IntermediateOperationType nextType) { | ||
// Anonymous sorts and lambdas can be swapped with a distinct operator to allow the inclusion | ||
// of the distinct operation in the query. | ||
if (type == FILTER) { | ||
return nextType == FILTER || nextType == SORTED || nextType == DISTINCT; | ||
} else if (type == SORTED) { | ||
return nextType == FILTER || nextType == DISTINCT; | ||
} else if (type == DISTINCT) { | ||
return nextType == FILTER || nextType == SORTED; | ||
} | ||
return false; | ||
} | ||
|
||
private int swapOperations(final LinkedList<IntermediateOperation<?, ?>> intermediateOperations, | ||
final int index1, final int index2) | ||
{ | ||
if (0 <= index1 && index1 < intermediateOperations.size() && 0 <= index2 && index2 < intermediateOperations.size()) { | ||
final IntermediateOperation<?, ?> io1 = intermediateOperations.get(index1); | ||
final IntermediateOperation<?, ?> io2 = intermediateOperations.get(index2); | ||
|
||
intermediateOperations.set(index1, io2); | ||
intermediateOperations.set(index2, io1); | ||
|
||
return index2; | ||
} | ||
return -1; | ||
} | ||
|
||
private boolean anonymousLambda(final IntermediateOperation<?, ?> operation) { | ||
if (operation.type() == DISTINCT){ | ||
return true; | ||
} | ||
final Object[] arguments = operation.arguments(); | ||
return !(arguments != null && (arguments[0] instanceof SpeedmentPredicate || arguments[0] instanceof Field)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
...edment/jpastreamer/interopoptimizer/standard/internal/strategy/InternalOptimizerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package com.speedment.jpastreamer.interopoptimizer.standard.internal.strategy; | ||
|
||
import com.speedment.jpastreamer.pipeline.Pipeline; | ||
import com.speedment.jpastreamer.pipeline.PipelineFactory; | ||
import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperation; | ||
import com.speedment.jpastreamer.pipeline.intermediate.IntermediateOperationFactory; | ||
import com.speedment.jpastreamer.rootfactory.RootFactory; | ||
import org.junit.jupiter.api.DynamicTest; | ||
import org.junit.jupiter.api.TestFactory; | ||
|
||
import java.lang.reflect.Field; | ||
import java.util.List; | ||
import java.util.ServiceLoader; | ||
import java.util.stream.Stream; | ||
|
||
import static org.junit.jupiter.api.Assertions.*; | ||
import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
import static org.junit.jupiter.api.DynamicTest.dynamicTest; | ||
|
||
abstract class InternalOptimizerTest<ENTITY> { | ||
|
||
protected final PipelineFactory pipelineFactory = RootFactory.getOrThrow(PipelineFactory.class, ServiceLoader::load); | ||
protected final IntermediateOperationFactory iof = RootFactory.getOrThrow(IntermediateOperationFactory.class, ServiceLoader::load); | ||
protected final MoveAnonymousLambdaOperations optimizer = new MoveAnonymousLambdaOperations(); | ||
|
||
abstract Class<ENTITY> getEntityClass(); | ||
|
||
@TestFactory | ||
Stream<DynamicTest> modify() { | ||
return pipelines().map(testCase -> dynamicTest(testCase.getName(), () -> { | ||
this.optimizer.optimize(testCase.getPipeline()); | ||
|
||
assertTestCase(testCase); | ||
})); | ||
} | ||
|
||
protected abstract Stream<PipelineTestCase<ENTITY>> pipelines(); | ||
|
||
protected Pipeline<ENTITY> createPipeline(final IntermediateOperation<?, ?>... operations) { | ||
final Pipeline<ENTITY> pipeline = pipelineFactory.createPipeline(getEntityClass()); | ||
|
||
for (IntermediateOperation<?, ?> operation : operations) { | ||
pipeline.intermediateOperations().add(operation); | ||
} | ||
|
||
return pipeline; | ||
} | ||
|
||
private void assertTestCase(final PipelineTestCase<ENTITY> testCase) { | ||
final Pipeline<ENTITY> unoptimized = testCase.getPipeline(); | ||
final Pipeline<ENTITY> optimized = testCase.getExpectedPipeline(); | ||
|
||
final List<IntermediateOperation<?, ?>> unoptimizedIntermediateOperations = unoptimized.intermediateOperations(); | ||
final List<IntermediateOperation<?, ?>> optimizedIntermediateOperations = optimized.intermediateOperations(); | ||
|
||
assertEquals(optimizedIntermediateOperations.size(), unoptimizedIntermediateOperations.size()); | ||
|
||
for (int i = 0; i < unoptimizedIntermediateOperations.size(); i++) { | ||
final IntermediateOperation<?, ?> unoptimizedOperation = unoptimizedIntermediateOperations.get(i); | ||
final IntermediateOperation<?, ?> optimizedOperation = optimizedIntermediateOperations.get(i); | ||
|
||
assertEquals(optimizedOperation.type(), unoptimizedOperation.type()); | ||
assertArguments(optimizedOperation.arguments(), unoptimizedOperation.arguments()); | ||
} | ||
} | ||
|
||
protected void assertArguments(final Object[] expected, final Object[] actual) { | ||
if (expected != actual) { | ||
assertNotNull(expected); | ||
assertNotNull(actual); | ||
assertEquals(expected.length, actual.length); | ||
|
||
for(int i = 0; i < expected.length; ++i) { | ||
Object expectedElement = expected[i]; | ||
Object actualElement = actual[i]; | ||
if (expectedElement != actualElement) { | ||
final Field[] actualDeclaredFields = actualElement.getClass().getDeclaredFields(); | ||
final Field[] expectedDeclaredFields = expectedElement.getClass().getDeclaredFields(); | ||
for (int j = 0; j < expectedDeclaredFields.length; ++j) { | ||
assertEquals(actualDeclaredFields[i], expectedDeclaredFields[i]); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
protected static final class PipelineTestCase<ENTITY> { | ||
|
||
private final String name; | ||
private final Pipeline<ENTITY> pipeline; | ||
private final Pipeline<ENTITY> expectedPipeline; | ||
|
||
protected PipelineTestCase( | ||
final String name, | ||
final Pipeline<ENTITY> pipeline, | ||
final Pipeline<ENTITY> expectedPipeline | ||
) { | ||
this.name = name; | ||
this.pipeline = pipeline; | ||
this.expectedPipeline = expectedPipeline; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
public Pipeline<ENTITY> getPipeline() { | ||
return pipeline; | ||
} | ||
|
||
public Pipeline<ENTITY> getExpectedPipeline() { | ||
return expectedPipeline; | ||
} | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.