Skip to content

Commit 5350e61

Browse files
committed
Enh 39023807 - [39023805->14.1.2.0.6] Improve range filters performance via direct forward index evaluation
[git-p4: depot-paths = "//dev/coherence-ce/release/coherence-ce-v14.1.2.0/": change = 119073]
1 parent 5f71dd7 commit 5350e61

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
@@ -17,13 +17,16 @@
1717

1818
import java.util.Collection;
1919
import java.util.HashSet;
20+
import java.util.Iterator;
2021
import java.util.Map;
2122
import java.util.NoSuchElementException;
2223
import java.util.Set;
2324
import java.util.NavigableMap;
2425

2526
import static com.tangosol.util.filter.ExtractorFilter.ensureSafeSet;
27+
import static com.tangosol.util.filter.ExtractorFilter.isForwardIndexSupported;
2628
import static com.tangosol.util.filter.ExtractorFilter.isInapplicableIndex;
29+
import static com.tangosol.util.filter.ExtractorFilter.shouldEvaluateUsingForwardIndex;
2730

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

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

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

448459
for (Map.Entry<E, Set<?>> entry : mapRange.entrySet())
@@ -460,6 +471,36 @@ protected void applySortedIndex(MapIndex index, Set setKeys, NavigableMap<E, Set
460471
}
461472
}
462473

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

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
@@ -29,6 +29,7 @@
2929
import java.io.IOException;
3030

3131
import java.util.ArrayList;
32+
import java.util.Collection;
3233
import java.util.Collections;
3334
import java.util.HashSet;
3435
import java.util.List;
@@ -289,6 +290,73 @@ protected static boolean isIncompletePartitionedIndex(MapIndex index)
289290
&& index.isPartial();
290291
}
291292

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

294362
/**
@@ -298,6 +366,12 @@ protected static boolean isIncompletePartitionedIndex(MapIndex index)
298366
*/
299367
public static int EVAL_COST = 1000;
300368

369+
/**
370+
* Cardinality factor used to determine when to switch from inverse-index
371+
* retain/remove to forward-index key evaluation.
372+
*/
373+
public static int FORWARD_INDEX_EVAL_CARDINALITY_FACTOR = 1;
374+
301375

302376
// ----- data members ---------------------------------------------------
303377

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)