Skip to content

Commit cf6cfc9

Browse files
committed
Enh 36428544 - [36428529->24.09] Reduce the overhead of key set manipulation when applying indexes
[git-p4: depot-paths = "//dev/coherence-ce/main/": change = 107792]
1 parent 432271f commit cf6cfc9

File tree

12 files changed

+224
-129
lines changed

12 files changed

+224
-129
lines changed

prj/coherence-core/src/main/java/com/tangosol/internal/util/UnsafeSubSet.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
/*
2-
* Copyright (c) 2000, 2023, Oracle and/or its affiliates.
2+
* Copyright (c) 2000, 2024, Oracle and/or its affiliates.
33
*
44
* Licensed under the Universal Permissive License v 1.0 as shown at
55
* https://oss.oracle.com/licenses/upl.
66
*/
77

88
package com.tangosol.internal.util;
99

10+
import com.tangosol.util.Base;
1011
import com.tangosol.util.SubSet;
1112

1213
import com.tangosol.net.BackingMapManagerContext;
@@ -16,7 +17,6 @@
1617

1718
import java.util.ArrayList;
1819
import java.util.Collection;
19-
import java.util.HashSet;
2020
import java.util.List;
2121
import java.util.Set;
2222

@@ -244,7 +244,7 @@ protected Set<E> collectSet(Collection<E> col)
244244
// partitions requested (parts) and the service owned partitions equals
245245
// each other. However, since there might be a transfer in-flight, the
246246
// collection could contain keys from "unowned" partitions
247-
Set<E> set = new HashSet<E>(col.size());
247+
Set<E> set = Base.newHashSet(col.size());
248248
for (E key : col)
249249
{
250250
if (parts.contains(ctx.getKeyPartition(key)))

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

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2023, Oracle and/or its affiliates.
2+
* Copyright (c) 2000, 2024, Oracle and/or its affiliates.
33
*
44
* Licensed under the Universal Permissive License v 1.0 as shown at
55
* https://oss.oracle.com/licenses/upl.
@@ -46,6 +46,8 @@
4646

4747
import java.util.Arrays;
4848
import java.util.Collection;
49+
import java.util.HashMap;
50+
import java.util.HashSet;
4951
import java.util.Iterator;
5052
import java.util.List;
5153
import java.util.Random;
@@ -59,6 +61,86 @@
5961
@SuppressWarnings({"unused", "deprecation"})
6062
public abstract class Base
6163
{
64+
// ----- factory methods for hashing collections ------------------------
65+
66+
/**
67+
* Construct an instance of {@link HashSet} with a desired number of elements
68+
* and a load factor.
69+
*
70+
* @param cElements the desired number of elements
71+
* @param flLoadFactor the load factor
72+
*
73+
* @return an instance of a {@code HashSet}
74+
*
75+
* @param <E> the type of set elements
76+
*/
77+
public static <E> HashSet<E> newHashSet(int cElements, float flLoadFactor)
78+
{
79+
return new HashSet<>(calculateCapacity(cElements, flLoadFactor), flLoadFactor);
80+
}
81+
82+
/**
83+
* Construct an instance of {@link HashSet} with a desired number of elements
84+
* and a default load factor.
85+
*
86+
* @param cElements the desired number of elements
87+
*
88+
* @return an instance of a {@code HashSet}
89+
*
90+
* @param <E> the type of set elements
91+
*/
92+
public static <E> HashSet<E> newHashSet(int cElements)
93+
{
94+
return newHashSet(cElements, DEFAULT_LOAD_FACTOR);
95+
}
96+
97+
/**
98+
* Construct an instance of {@link HashMap} with a desired number of entries
99+
* and a load factor.
100+
*
101+
* @param cEntries the desired number of entries
102+
* @param flLoadFactor the load factor
103+
*
104+
* @return an instance of a {@code HashMap}
105+
*
106+
* @param <K> the type of map keys
107+
* @param <V> the type of map values
108+
*/
109+
public static <K, V> HashMap<K, V> newHashMap(int cEntries, float flLoadFactor)
110+
{
111+
return new HashMap<>(calculateCapacity(cEntries, flLoadFactor), flLoadFactor);
112+
}
113+
114+
/**
115+
* Construct an instance of {@link HashMap} with a desired number of entries
116+
* and a default load factor (0.75).
117+
*
118+
* @param cEntries the desired number of entries
119+
*
120+
* @return an instance of a {@code HashMap}
121+
*
122+
* @param <K> the type of map keys
123+
* @param <V> the type of map values
124+
*/
125+
public static <K, V> HashMap<K, V> newHashMap(int cEntries)
126+
{
127+
return newHashMap(cEntries, DEFAULT_LOAD_FACTOR);
128+
}
129+
130+
/**
131+
* Calculate capacity of a HashSet or HashMap that will require no rehashing
132+
* unless the specified maximum number of elements/entries is exceeded.
133+
*
134+
* @param cMaxElements the maximum expected number of elements/entries
135+
* @param dblLoadFactor the desired load factor
136+
*
137+
* @return a capacity to create a HashSet or a HashMap with
138+
*/
139+
private static int calculateCapacity(int cMaxElements, double dblLoadFactor)
140+
{
141+
return (int) Math.ceil(cMaxElements / dblLoadFactor);
142+
}
143+
62144
// ----- debugging support: expression evaluation ----------------------
63145

64146
/**
@@ -2881,6 +2963,11 @@ public void println()
28812963
*/
28822964
public static final int LOG_QUIET = 6;
28832965

2966+
/**
2967+
* Default load factor for hashing collections.
2968+
*/
2969+
public static final float DEFAULT_LOAD_FACTOR = 0.75f;
2970+
28842971
// ----- data members ---------------------------------------------------
28852972

28862973
/**

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2023, Oracle and/or its affiliates.
2+
* Copyright (c) 2000, 2024, Oracle and/or its affiliates.
33
*
44
* Licensed under the Universal Permissive License v 1.0 as shown at
55
* https://oss.oracle.com/licenses/upl.
@@ -21,6 +21,7 @@
2121
* @author hr 2014.02.19
2222
* @since Coherence 12.1.2
2323
*/
24+
@SuppressWarnings("unchecked")
2425
public class ChainedCollection<E>
2526
extends AbstractCollection<E>
2627
{
@@ -34,7 +35,7 @@ public class ChainedCollection<E>
3435
*/
3536
public ChainedCollection(Collection<Collection<E>> col)
3637
{
37-
this(col.toArray(new Collection[col.size()]));
38+
this(col.toArray(Collection[]::new));
3839
}
3940

4041
/**
@@ -70,8 +71,7 @@ public ChainedCollection(ChainedCollection<E> original, Collection<E>... aCol)
7071
*
7172
* @param aCol an array of Collection objects
7273
*/
73-
@SafeVarargs
74-
public ChainedCollection(Collection<E>... aCol)
74+
public ChainedCollection(Collection<E>[] aCol)
7575
{
7676
f_aCol = aCol;
7777
}

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

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
package com.tangosol.util.filter;
99

1010

11+
import com.tangosol.util.ChainedCollection;
1112
import com.tangosol.util.Filter;
1213
import com.tangosol.util.QueryContext;
1314
import com.tangosol.util.QueryRecord;
1415
import com.tangosol.util.SubSet;
1516

1617
import java.util.ArrayList;
18+
import java.util.Collection;
1719
import java.util.HashSet;
1820
import java.util.LinkedHashSet;
1921
import java.util.List;
@@ -26,6 +28,7 @@
2628
*
2729
* @author cp/gg 2002.11.01
2830
*/
31+
@SuppressWarnings({"unchecked", "rawtypes"})
2932
public class AnyFilter
3033
extends ArrayFilter
3134
{
@@ -152,35 +155,41 @@ protected Filter applyIndex(Map mapIndexes, Set setKeys, QueryContext ctx,
152155
{
153156
optimizeFilterOrder(mapIndexes, setKeys);
154157

155-
Filter[] aFilter = getFilters();
156-
int cFilters = aFilter.length;
157-
List listFilter = new ArrayList(cFilters);
158-
Set setMatch = new HashSet(setKeys.size());
158+
Filter[] aFilter = getFilters();
159+
int cFilters = aFilter.length;
159160

160-
// listFilter is an array of filters that will have to be re-applied
161-
// setMatch is an accumulating set of already matching keys
161+
// a list of filters that will have to be re-applied
162+
List<Filter<?>> listFilter = new ArrayList<>(cFilters);
162163

163-
SubSet setRemain = new SubSet(setKeys);
164-
for (int i = cFilters - 1; i >= 0; i--) // iterate backwards, to reduce the set of remaining keys as quickly as possible
164+
// a list of sets of matching keys, which will be subsequently merged
165+
// into a set of keys that should be retained; doing it this way, instead
166+
// of simply creating a set and calling addAll on it for each filter
167+
// avoids set resizing and rehashing of the keys
168+
List<Set<?>> listMatch = new ArrayList<>(cFilters);
169+
170+
// iterate backwards, from the least selective filter to the most selective one,
171+
// to ensure that the largest sets of matching keys are at the beginning
172+
// of a ChainedCollection, making subsequent retainAll call faster
173+
for (int i = cFilters - 1; i >= 0; i--)
165174
{
166175
Filter filter = aFilter[i];
167176
if (filter instanceof IndexAwareFilter)
168177
{
178+
SubSet setRemain = new SubSet(setKeys);
169179
Filter filterDefer = applyFilter(filter, i, mapIndexes, setRemain, ctx, step);
170-
Set setRemoved = setRemain.getRemoved();
171180
Set setRetained = setRemain.getRetained();
172181

173182
if (filterDefer == null)
174183
{
175-
// these are definitely "in"
176-
setMatch.addAll(setRetained);
177-
178-
// reset setRemain to contain only the keys that were removed
179-
setRemain = new SubSet(setRemoved);
184+
if (!setRetained.isEmpty())
185+
{
186+
// these are definitely "in"
187+
listMatch.add(setRetained);
188+
}
180189
}
181190
else
182191
{
183-
if (!setRemoved.isEmpty())
192+
if (!setRemain.getRemoved().isEmpty())
184193
{
185194
// some keys are definitely "out" for this filter;
186195
// we need to incorporate this knowledge into a deferred
@@ -201,11 +210,6 @@ protected Filter applyIndex(Map mapIndexes, Set setKeys, QueryContext ctx,
201210
{
202211
listFilter.add(filterDefer);
203212
}
204-
205-
// we have to reset setRemain it to contain all the keys
206-
// that are not definite matches
207-
setRemain = new SubSet(setKeys);
208-
setRemain.removeAll(setMatch);
209213
}
210214
}
211215
else if (filter != null)
@@ -214,37 +218,38 @@ else if (filter != null)
214218
}
215219
}
216220

217-
int cMatches = setMatch.size();
221+
// create a set containing all the matches identified by individual filters
222+
Set<?> setMatches = new HashSet<>(new ChainedCollection<>(listMatch.toArray(Set[]::new)));
218223

219224
cFilters = listFilter.size();
220225
if (cFilters == 0)
221226
{
222-
if (cMatches > 0)
227+
if (setMatches.isEmpty())
223228
{
224-
setKeys.retainAll(setMatch);
229+
setKeys.clear();
225230
}
226231
else
227232
{
228-
setKeys.clear();
233+
setKeys.retainAll(setMatches);
229234
}
230235
return null;
231236
}
232-
else if (cFilters == 1 && cMatches == 0)
237+
else if (cFilters == 1 && setMatches.isEmpty())
233238
{
234-
return (Filter) listFilter.get(0);
239+
return listFilter.get(0);
235240
}
236241
else
237242
{
238-
if (cMatches > 0)
243+
if (!setMatches.isEmpty())
239244
{
240245
// the keys that have been matched are definitely "in";
241246
// the remaining keys each need to be evaluated later
242-
KeyFilter filterKey = new KeyFilter(setMatch);
247+
KeyFilter filterKey = new KeyFilter(setMatches);
243248
listFilter.add(0, filterKey);
244249
cFilters++;
245250
}
246251

247-
return new AnyFilter((Filter[]) listFilter.toArray(new Filter[cFilters]));
252+
return new AnyFilter(listFilter.toArray(new Filter[cFilters]));
248253
}
249254
}
250255

0 commit comments

Comments
 (0)