Skip to content

Commit e728edd

Browse files
committed
addresses #212, but the relevant code paths are commented out at the moment (until we have time for some experimentation to better understand the behavior of it)
1 parent b8e0922 commit e728edd

File tree

2 files changed

+180
-9
lines changed

2 files changed

+180
-9
lines changed

src/main/java/se/liu/ida/hefquin/engine/queryplan/executable/impl/pushbased/PushBasedExecPlanTaskForBinaryOperator.java

+90-5
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,19 @@ protected ExecutableOperator getExecOp() {
4040
@Override
4141
protected void produceOutput( final IntermediateResultElementSink sink )
4242
throws ExecOpExecutionException, ExecPlanTaskInputException, ExecPlanTaskInterruptionException {
43+
if ( op.requiresCompleteChild1InputFirst() )
44+
produceOutputByConsumingInput1First(sink);
45+
else
46+
//produceOutputByConsumingBothInputsInParallel(sink);
47+
produceOutputByConsumingInput1First(sink);
48+
}
4349

44-
// Attention: the current implementation of this method ignores
45-
// whether op.requiresCompleteChild1InputFirst() is true or
46-
// false but, instead, simply consumes and pushes the complete
47-
// child 1 input first (as would be required in case that the
48-
// aforementioned function returns true).
50+
/**
51+
* Consumes the complete child 1 input first (and pushes that input to the
52+
* operator {@link #op}), before moving on to the input from child 2.
53+
*/
54+
protected void produceOutputByConsumingInput1First( final IntermediateResultElementSink sink )
55+
throws ExecOpExecutionException, ExecPlanTaskInputException, ExecPlanTaskInterruptionException {
4956

5057
boolean input1Consumed = false;
5158
while ( ! input1Consumed ) {
@@ -72,4 +79,82 @@ protected void produceOutput( final IntermediateResultElementSink sink )
7279
}
7380
}
7481

82+
/**
83+
* Aims to consume both inputs in parallel.
84+
*/
85+
protected void produceOutputByConsumingBothInputsInParallel( final IntermediateResultElementSink sink )
86+
throws ExecOpExecutionException, ExecPlanTaskInputException, ExecPlanTaskInterruptionException {
87+
88+
boolean nextWaitForInput1 = true; // flag to switch between waiting for input 1 versus input 2
89+
boolean input1Consumed = false;
90+
boolean input2Consumed = false;
91+
while ( ! input1Consumed || ! input2Consumed ) {
92+
// Before blindly asking any of the two inputs to give us its next
93+
// IntermediateResultBlock (which may cause this thread to wait if
94+
// no such block is available at the moment), let's first ask them
95+
// if they currently have a block available. If so, request the next
96+
// block from the input that says it has a block available.
97+
boolean blockConsumed = false;
98+
if ( ! input1Consumed && input1.hasNextIntermediateResultBlockAvailable() )
99+
{
100+
// calling 'getNextIntermediateResultBlock()' should not cause this thread to wait
101+
final IntermediateResultBlock nextInputBlock = input1.getNextIntermediateResultBlock();
102+
if ( nextInputBlock != null ) {
103+
op.processBlockFromChild1(nextInputBlock, sink, execCxt);
104+
}
105+
106+
blockConsumed = true;
107+
}
108+
109+
if ( ! input2Consumed && input2.hasNextIntermediateResultBlockAvailable() )
110+
{
111+
// calling 'getNextIntermediateResultBlock()' should not cause this thread to wait
112+
final IntermediateResultBlock nextInputBlock = input2.getNextIntermediateResultBlock();
113+
if ( nextInputBlock != null ) {
114+
op.processBlockFromChild2(nextInputBlock, sink, execCxt);
115+
}
116+
117+
blockConsumed = true;
118+
}
119+
120+
if ( ! blockConsumed ) {
121+
// If none of the two inputs had a block available at the
122+
// moment, we ask one of them to produce its next block,
123+
// which may cause this thread to wait until that next
124+
// block has been produced. To decide which of the two
125+
// inputs we ask (and, then, wait for) we use a round
126+
// robin approach (i.e., always switch between the two
127+
// inputs). To this end, we use the 'nextWaitForInput1'
128+
// flag: if that flag is true, we will next ask (and wait
129+
// for) input 1; if that flag is false, we will next ask
130+
// (and wait for) input 2.
131+
if ( nextWaitForInput1 && ! input1Consumed ) {
132+
// calling 'getNextIntermediateResultBlock()' may cause this thread to wait
133+
final IntermediateResultBlock nextInputBlock = input1.getNextIntermediateResultBlock();
134+
if ( nextInputBlock != null ) {
135+
op.processBlockFromChild1(nextInputBlock, sink, execCxt);
136+
}
137+
else {
138+
op.wrapUpForChild1(sink, execCxt);
139+
input1Consumed = true;
140+
}
141+
}
142+
else if ( ! input2Consumed ) {
143+
// calling 'getNextIntermediateResultBlock()' may cause this thread to wait
144+
final IntermediateResultBlock nextInputBlock = input2.getNextIntermediateResultBlock();
145+
if ( nextInputBlock != null ) {
146+
op.processBlockFromChild2(nextInputBlock, sink, execCxt);
147+
}
148+
else {
149+
op.wrapUpForChild2(sink, execCxt);
150+
input2Consumed = true;
151+
}
152+
}
153+
// flip the 'nextWaitForInput1' flag so that, next time we
154+
// have to wait, we will wait for the respective other input
155+
nextWaitForInput1 = ! nextWaitForInput1;
156+
}
157+
}
158+
}
159+
75160
}

src/main/java/se/liu/ida/hefquin/engine/queryplan/executable/impl/pushbased/PushBasedExecPlanTaskForNaryOperator.java

+90-4
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,19 @@ protected ExecutableOperator getExecOp() {
3737
@Override
3838
protected void produceOutput( final IntermediateResultElementSink sink )
3939
throws ExecOpExecutionException, ExecPlanTaskInputException, ExecPlanTaskInterruptionException {
40+
produceOutputByConsumingAllInputsInParallel(sink);
41+
//produceOutputByConsumingInputsOneAfterAnother(sink);
42+
}
4043

41-
// Attention: the current implementation of this method simply consumes
42-
// and pushes the complete i-th child input first before moving on to
43-
// the (i+1)-th child. Hence, with this implementation we do not
44-
// actually benefit from the parallelization.
44+
/**
45+
* Consumes the complete i-th input first (and pushes that input to the
46+
* operator {@link #op}), before moving on to the (i+1)-th input. Hence,
47+
* this implementation does not consume the inputs in parallel. Instead,
48+
* if one of the inputs requires a long time, no progress is made in
49+
* parallel based on any of the other inputs.
50+
*/
51+
protected void produceOutputByConsumingInputsOneAfterAnother( final IntermediateResultElementSink sink )
52+
throws ExecOpExecutionException, ExecPlanTaskInputException, ExecPlanTaskInterruptionException {
4553

4654
for ( int i = 0; i < inputs.length; i++ ) {
4755
boolean inputConsumed = false;
@@ -58,4 +66,82 @@ protected void produceOutput( final IntermediateResultElementSink sink )
5866
}
5967
}
6068

69+
70+
/**
71+
* Consumes the complete i-th input first (and pushes that input to the
72+
* operator {@link #op}), before moving on to the (i+1)-th input.
73+
*/
74+
protected void produceOutputByConsumingAllInputsInParallel( final IntermediateResultElementSink sink )
75+
throws ExecOpExecutionException, ExecPlanTaskInputException, ExecPlanTaskInterruptionException {
76+
77+
final boolean[] inputConsumed = new boolean[inputs.length];
78+
for ( int i = 0; i < inputs.length; i++ ) { inputConsumed[i] = false; }
79+
80+
int indexOfNextInputToWaitFor = 0;
81+
int numberOfInputsConsumed = 0;
82+
while ( numberOfInputsConsumed < inputs.length ) {
83+
// Before blindly asking any of the inputs to give us its next
84+
// IntermediateResultBlock (which may cause this thread to wait
85+
// if no such block is available at the moment), let's first ask
86+
// them if they currently have a block available. If so, request
87+
// the next block from the input that says it has a block available.
88+
boolean blockConsumed = false;
89+
for ( int i = 0; i < inputs.length; i++ ) {
90+
if ( ! inputConsumed[i] && inputs[i].hasNextIntermediateResultBlockAvailable() ) {
91+
// calling 'getNextIntermediateResultBlock()' should not cause this thread to wait
92+
final IntermediateResultBlock nextInputBlock = inputs[i].getNextIntermediateResultBlock();
93+
if ( nextInputBlock != null ) {
94+
op.processBlockFromXthChild(i, nextInputBlock, sink, execCxt);
95+
}
96+
97+
blockConsumed = true;
98+
}
99+
}
100+
101+
if ( ! blockConsumed ) {
102+
// If none of the inputs had a block available at the moment,
103+
// we ask one of them to produce its next block, which may
104+
// cause this thread to wait until that next block has been
105+
// produced. To decide which of the inputs we ask (and, then,
106+
// wait for) we use a round robin approach. To this end, we
107+
// use the 'indexOfNextInputToWaitFor' pointer which we advance
108+
// each time we leave this code block here.
109+
110+
// First, we have to make sure that 'indexOfNextInputToWaitFor'
111+
// points to an input that has not been consumed completely yet.
112+
while ( inputConsumed[indexOfNextInputToWaitFor] == true ) {
113+
indexOfNextInputToWaitFor = advanceIndexOfInput(indexOfNextInputToWaitFor);
114+
}
115+
116+
// Now we ask that input to produce its next block, which may
117+
// cause this thread to wait.
118+
final IntermediateResultBlock nextInputBlock = inputs[indexOfNextInputToWaitFor].getNextIntermediateResultBlock();
119+
if ( nextInputBlock != null ) {
120+
op.processBlockFromXthChild(indexOfNextInputToWaitFor, nextInputBlock, sink, execCxt);
121+
}
122+
else {
123+
op.wrapUpForXthChild(indexOfNextInputToWaitFor, sink, execCxt);
124+
inputConsumed[indexOfNextInputToWaitFor] = true;
125+
numberOfInputsConsumed++;
126+
}
127+
128+
// Finally, we advance the 'indexOfNextInputToWaitFor' pointer
129+
// so that, next time we will have to wait, we will wait for
130+
// the next input (rather than always waiting for the same
131+
// input before moving on to the next input).
132+
indexOfNextInputToWaitFor = advanceIndexOfInput(indexOfNextInputToWaitFor);
133+
}
134+
}
135+
}
136+
137+
/**
138+
* Returns the given integer increased by one, unless such an
139+
* increase results in an integer that is outside of the bounds
140+
* of the {@link #inputs} array, in which case the function returns
141+
* zero (effectively jumping back to the first index in the array).
142+
*/
143+
protected int advanceIndexOfInput( final int currentIndex ) {
144+
final int i = currentIndex + 1;
145+
return ( i < inputs.length ) ? i : 0;
146+
}
61147
}

0 commit comments

Comments
 (0)