Skip to content

Commit 0b16be4

Browse files
committed
Enh 39023810 - [39023805->25.09] Improve range filters performance via direct forward index evaluation
[git-p4: depot-paths = "//dev/coherence-ce/main/": change = 119069]
1 parent 1e7987c commit 0b16be4

File tree

7 files changed

+433
-22
lines changed

7 files changed

+433
-22
lines changed

prj/coherence-core/src/main/java/com/tangosol/util/filter/BetweenFilter.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@
1919

2020
import java.util.Collection;
2121
import java.util.HashSet;
22+
import java.util.Iterator;
2223
import java.util.Map;
2324
import java.util.Set;
2425
import java.util.NavigableMap;
2526

2627
import static com.tangosol.util.filter.ExtractorFilter.ensureSafeSet;
28+
import static com.tangosol.util.filter.ExtractorFilter.isForwardIndexSupported;
2729
import static com.tangosol.util.filter.ExtractorFilter.isInapplicableIndex;
30+
import static com.tangosol.util.filter.ExtractorFilter.shouldEvaluateUsingForwardIndex;
2831

2932
/**
3033
* Filter which compares the result of a method invocation with a value for
@@ -444,6 +447,14 @@ protected void applySortedIndex(MapIndex index, Set setKeys, NavigableMap<E, Set
444447

445448
NavigableMap<E, Set<?>> mapRange = mapContents.subMap(getLowerBound(), isLowerBoundInclusive(), getUpperBound(), isUpperBoundInclusive());
446449

450+
if (!index.isPartial()
451+
&& isForwardIndexSupported(index, setKeys)
452+
&& shouldEvaluateUsingForwardIndex(setKeys, mapRange.values()))
453+
{
454+
applyForwardIndex(index, setKeys);
455+
return;
456+
}
457+
447458
Collection colKeysToRetain = new HashSet<>();
448459

449460
for (Map.Entry<E, Set<?>> entry : mapRange.entrySet())
@@ -461,6 +472,36 @@ protected void applySortedIndex(MapIndex index, Set setKeys, NavigableMap<E, Set
461472
}
462473
}
463474

475+
/**
476+
* Apply this filter using forward-index lookups for each candidate key.
477+
*
478+
* @param index the index
479+
* @param setKeys the candidate key set
480+
*/
481+
protected void applyForwardIndex(MapIndex index, Set setKeys)
482+
{
483+
for (Iterator iterator = setKeys.iterator(); iterator.hasNext(); )
484+
{
485+
Object oKey = iterator.next();
486+
Object oExtracted = index.get(oKey);
487+
boolean fMatch;
488+
489+
try
490+
{
491+
fMatch = oExtracted != MapIndex.NO_VALUE && evaluateExtracted(oExtracted);
492+
}
493+
catch (ClassCastException ignored)
494+
{
495+
fMatch = false;
496+
}
497+
498+
if (!fMatch)
499+
{
500+
iterator.remove();
501+
}
502+
}
503+
}
504+
464505
/**
465506
* Determine if the filter will match all or none of the entries in the index.
466507
*

prj/coherence-core/src/main/java/com/tangosol/util/filter/ExtractorFilter.java

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.io.IOException;
3131

3232
import java.util.ArrayList;
33+
import java.util.Collection;
3334
import java.util.Collections;
3435
import java.util.List;
3536
import java.util.Map;
@@ -298,6 +299,73 @@ protected static boolean isIncompletePartitionedIndex(MapIndex index)
298299
&& index.isPartial();
299300
}
300301

302+
/**
303+
* Return {@code true} if the supplied candidate and index-cardinality data
304+
* indicates that it is cheaper to evaluate keys using a forward index.
305+
*
306+
* @param setKeys the current candidate key set
307+
* @param colIndexSets the index sets contributing to the matching range
308+
*
309+
* @return {@code true} if forward-index evaluation should be used
310+
*/
311+
protected static boolean shouldEvaluateUsingForwardIndex(Set setKeys, Collection<? extends Set> colIndexSets)
312+
{
313+
return shouldEvaluateUsingForwardIndex(setKeys, colIndexSets, FORWARD_INDEX_EVAL_CARDINALITY_FACTOR);
314+
}
315+
316+
/**
317+
* Return {@code true} if the supplied candidate and index-cardinality data
318+
* indicates that it is cheaper to evaluate keys using a forward index.
319+
*
320+
* @param setKeys the current candidate key set
321+
* @param colIndexSets the index sets contributing to the matching range
322+
* @param nFactor the cardinality factor used to determine threshold
323+
*
324+
* @return {@code true} if forward-index evaluation should be used
325+
*/
326+
protected static boolean shouldEvaluateUsingForwardIndex(Set setKeys, Collection<? extends Set> colIndexSets,
327+
int nFactor)
328+
{
329+
if (setKeys == null || setKeys.isEmpty())
330+
{
331+
return false;
332+
}
333+
334+
long cThreshold = (long) setKeys.size() * nFactor;
335+
long cEntries = 0;
336+
337+
for (Set set : colIndexSets)
338+
{
339+
cEntries += ensureSafeSet(set).size();
340+
if (cEntries > cThreshold)
341+
{
342+
return true;
343+
}
344+
}
345+
346+
return false;
347+
}
348+
349+
/**
350+
* Return {@code true} if the supplied index appears to have forward-index
351+
* support for the supplied keys.
352+
*
353+
* @param index the index to test
354+
* @param setKeys the current candidate key set
355+
*
356+
* @return {@code true} if forward-index access is available
357+
*/
358+
protected static boolean isForwardIndexSupported(MapIndex index, Set setKeys)
359+
{
360+
if (index == null || setKeys == null || setKeys.isEmpty())
361+
{
362+
return false;
363+
}
364+
365+
Object oKey = setKeys.iterator().next();
366+
return index.get(oKey) != MapIndex.NO_VALUE;
367+
}
368+
301369
// ----- constants ------------------------------------------------------
302370

303371
/**
@@ -307,6 +375,12 @@ protected static boolean isIncompletePartitionedIndex(MapIndex index)
307375
*/
308376
public static int EVAL_COST = 1000;
309377

378+
/**
379+
* Cardinality factor used to determine when to switch from inverse-index
380+
* retain/remove to forward-index key evaluation.
381+
*/
382+
public static int FORWARD_INDEX_EVAL_CARDINALITY_FACTOR = 1;
383+
310384

311385
// ----- data members ---------------------------------------------------
312386

prj/coherence-core/src/main/java/com/tangosol/util/filter/GreaterFilter.java

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import com.tangosol.util.ValueExtractor;
1414

1515
import java.util.ArrayList;
16-
import java.util.Collection;
16+
import java.util.Iterator;
1717
import java.util.List;
1818
import java.util.Map;
1919
import java.util.NavigableMap;
@@ -217,21 +217,31 @@ else if (index.getIndexContents().isEmpty())
217217

218218
NavigableMap<E, Set<?>> mapHead = mapContents.headMap(value, !includeEquals());
219219
NavigableMap<E, Set<?>> mapTail = mapContents.tailMap(value, includeEquals());
220-
boolean fHeadHeavy = mapHead.size() > mapContents.size() / 2;
221-
if (fHeadHeavy)
220+
boolean fForward = isForwardIndexSupported(index, setKeys)
221+
&& shouldEvaluateUsingForwardIndex(setKeys, mapTail.values());
222+
223+
if (fForward)
222224
{
223-
List<Set<?>> listGT = new ArrayList<>(mapTail.size());
224-
for (Set<?> set : mapTail.values())
225-
{
226-
listGT.add(ensureSafeSet(set));
227-
}
228-
setKeys.retainAll(new ChainedCollection<>(listGT.toArray(Set[]::new)));
225+
applyForwardIndex(index, setKeys);
229226
}
230227
else
231228
{
232-
for (Set<?> set : mapHead.values())
229+
boolean fHeadHeavy = mapHead.size() > mapContents.size() / 2;
230+
if (fHeadHeavy)
233231
{
234-
setKeys.removeAll(ensureSafeSet(set));
232+
List<Set<?>> listGT = new ArrayList<>(mapTail.size());
233+
for (Set<?> set : mapTail.values())
234+
{
235+
listGT.add(ensureSafeSet(set));
236+
}
237+
setKeys.retainAll(new ChainedCollection<>(listGT.toArray(Set[]::new)));
238+
}
239+
else
240+
{
241+
for (Set<?> set : mapHead.values())
242+
{
243+
setKeys.removeAll(ensureSafeSet(set));
244+
}
235245
}
236246
}
237247

@@ -305,4 +315,34 @@ else if (evaluateExtracted(loEntry.getKey()))
305315
return null;
306316
}
307317

318+
/**
319+
* Apply this filter using forward-index lookups for each candidate key.
320+
*
321+
* @param index the index
322+
* @param setKeys the candidate key set
323+
*/
324+
protected void applyForwardIndex(MapIndex index, Set setKeys)
325+
{
326+
for (Iterator iterator = setKeys.iterator(); iterator.hasNext(); )
327+
{
328+
Object oKey = iterator.next();
329+
Object oExtracted = index.get(oKey);
330+
boolean fMatch;
331+
332+
try
333+
{
334+
fMatch = oExtracted != MapIndex.NO_VALUE && evaluateExtracted((E) oExtracted);
335+
}
336+
catch (ClassCastException ignored)
337+
{
338+
fMatch = false;
339+
}
340+
341+
if (!fMatch)
342+
{
343+
iterator.remove();
344+
}
345+
}
346+
}
347+
308348
}

prj/coherence-core/src/main/java/com/tangosol/util/filter/LessFilter.java

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import java.util.ArrayList;
1616
import java.util.Collection;
17+
import java.util.Iterator;
1718
import java.util.List;
1819
import java.util.Map;
1920
import java.util.Set;
@@ -218,26 +219,35 @@ else if (index.getIndexContents().isEmpty())
218219
Set setNULL = mapContents.get(null);
219220
NavigableMap<E, Set<?>> mapHead = mapContents.headMap(value, includeEquals());
220221
NavigableMap<E, Set<?>> mapTail = mapContents.tailMap(value, !includeEquals());
221-
boolean fHeadHeavy = mapHead.size() > mapContents.size() / 2;
222+
boolean fForward = isForwardIndexSupported(index, setKeys)
223+
&& shouldEvaluateUsingForwardIndex(setKeys, mapHead.values());
222224

223225
setKeys.removeAll(ensureSafeSet(setNULL));
224226

225-
if (fHeadHeavy)
227+
if (fForward)
226228
{
227-
for (Set<?> set : mapTail.values())
228-
{
229-
setKeys.removeAll(ensureSafeSet(set));
230-
}
229+
applyForwardIndex(index, setKeys);
231230
}
232231
else
233232
{
234-
List<Set<?>> listLT = new ArrayList<>(mapHead.size());
235-
for (Set<?> set : mapHead.values())
233+
boolean fHeadHeavy = mapHead.size() > mapContents.size() / 2;
234+
if (fHeadHeavy)
236235
{
237-
listLT.add(ensureSafeSet(set));
236+
for (Set<?> set : mapTail.values())
237+
{
238+
setKeys.removeAll(ensureSafeSet(set));
239+
}
238240
}
241+
else
242+
{
243+
List<Set<?>> listLT = new ArrayList<>(mapHead.size());
244+
for (Set<?> set : mapHead.values())
245+
{
246+
listLT.add(ensureSafeSet(set));
247+
}
239248

240-
setKeys.retainAll(new ChainedCollection<>(listLT.toArray(Set[]::new)));
249+
setKeys.retainAll(new ChainedCollection<>(listLT.toArray(Set[]::new)));
250+
}
241251
}
242252
}
243253
else
@@ -308,4 +318,34 @@ else if (evaluateExtracted(hiEntry.getKey()))
308318
return null;
309319
}
310320

321+
/**
322+
* Apply this filter using forward-index lookups for each candidate key.
323+
*
324+
* @param index the index
325+
* @param setKeys the candidate key set
326+
*/
327+
protected void applyForwardIndex(MapIndex index, Set setKeys)
328+
{
329+
for (Iterator iterator = setKeys.iterator(); iterator.hasNext(); )
330+
{
331+
Object oKey = iterator.next();
332+
Object oExtracted = index.get(oKey);
333+
boolean fMatch;
334+
335+
try
336+
{
337+
fMatch = oExtracted != MapIndex.NO_VALUE && evaluateExtracted((E) oExtracted);
338+
}
339+
catch (ClassCastException ignored)
340+
{
341+
fMatch = false;
342+
}
343+
344+
if (!fMatch)
345+
{
346+
iterator.remove();
347+
}
348+
}
349+
}
350+
311351
}

0 commit comments

Comments
 (0)