Skip to content

Commit bee9ef0

Browse files
committed
Bug 37878170 - [37873529->25.03.1] Optimize Subset for chainedcollections handling
RQ: job.9.20250425210711.7537 [git-p4: depot-paths = "//dev/coherence-ce/release/coherence-ce-v25.03/": change = 115941]
1 parent ba54157 commit bee9ef0

File tree

2 files changed

+138
-13
lines changed

2 files changed

+138
-13
lines changed

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

+11-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/*
2-
* Copyright (c) 2000, 2020, Oracle and/or its affiliates.
2+
* Copyright (c) 2000, 2025, Oracle and/or its affiliates.
33
*
44
* Licensed under the Universal Permissive License v 1.0 as shown at
5-
* http://oss.oracle.com/licenses/upl.
5+
* https://oss.oracle.com/licenses/upl.
66
*/
77

88
package com.tangosol.util;
@@ -496,8 +496,9 @@ public boolean removeAll(Collection<?> col)
496496
}
497497

498498
// if the percentage of removed is high enough (25% threshold)
499-
// switch to "retained"
500-
if (cOrig > 64 && (cRemove + cRemoved) > (cOrig >>> 2))
499+
// or the passed collection is chained, switch to "retained"
500+
if (!(col instanceof ChainedCollection<?>) &&
501+
cOrig > 64 && (cRemove + cRemoved) > (cOrig >>> 2))
501502
{
502503
return ensureRetained().removeAll(col);
503504
}
@@ -530,15 +531,16 @@ public boolean retainAll(Collection<?> col)
530531
// contains implementation is expected, i.e. col implements Set
531532

532533
Set setMod = m_setMod;
533-
int cPassed = col.size();
534534

535535
if (m_fRetained)
536536
{
537537
setMod = ensureRetained();
538538

539539
int cPrevSize = setMod.size();
540-
if (cPrevSize >= cPassed * 2)
540+
if (col instanceof ChainedCollection<?> || cPrevSize >= col.size() * 2)
541541
{
542+
// moving to a partitioned index makes iterating over the passed
543+
// collection always faster if it's a chained collection; or:
542544
// as the original set is at least twice the size of the passed
543545
// set it is cheaper to create a new retained set than remove
544546
// from the original
@@ -573,7 +575,9 @@ public boolean retainAll(Collection<?> col)
573575
}
574576

575577
// optimization: decide on the cheaper walk (original or passed)
576-
if (cOrig >= cPassed)
578+
// as moving to a partitioned index makes iterating over the passed
579+
// collection always faster
580+
if (col instanceof ChainedCollection<?> || cOrig > col.size())
577581
{
578582
retainAllInternal(col, setOrig, setRemoved);
579583
}

prj/test/functional/filter/src/main/java/filter/MiscTests.java

+127-6
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
/*
2-
* Copyright (c) 2000, 2024, Oracle and/or its affiliates.
2+
* Copyright (c) 2000, 2025, 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 filter;
99

10+
import com.oracle.coherence.common.collections.NullableSortedMap;
1011
import com.oracle.coherence.testing.AbstractFunctionalTest;
11-
import com.tangosol.coherence.config.Config;
12+
13+
import com.tangosol.util.Binary;
14+
import com.tangosol.util.ChainedCollection;
15+
import com.tangosol.util.SubSet;
1216
import com.tangosol.util.comparator.SafeComparator;
1317
import com.tangosol.util.filter.AlwaysFilter;
1418
import com.tangosol.util.filter.LimitFilter;
15-
import data.Person;
1619

1720
import com.tangosol.net.CacheFactory;
1821
import com.tangosol.net.NamedCache;
@@ -24,15 +27,28 @@
2427
import com.tangosol.util.filter.AllFilter;
2528
import com.tangosol.util.filter.BetweenFilter;
2629

30+
import data.Person;
31+
32+
import java.nio.charset.StandardCharsets;
33+
34+
import java.util.ArrayList;
35+
import java.util.Collection;
36+
import java.util.Collections;
2737
import java.util.Comparator;
2838
import java.util.HashMap;
39+
import java.util.HashSet;
40+
import java.util.Iterator;
41+
import java.util.List;
2942
import java.util.Map;
30-
43+
import java.util.Set;
3144
import java.util.SortedSet;
45+
46+
import org.junit.Assert;
3247
import org.junit.BeforeClass;
3348
import org.junit.Test;
3449

3550
import static junit.framework.Assert.fail;
51+
3652
import static org.junit.Assert.assertEquals;
3753

3854
public class MiscTests
@@ -58,6 +74,19 @@ public static void _startup()
5874
}
5975

6076
// ----- test methods ---------------------------------------------------
77+
78+
@Test
79+
public void testRetainAll()
80+
{
81+
testSubset(true);
82+
}
83+
84+
@Test
85+
public void testRemoveAll()
86+
{
87+
testSubset(false);
88+
}
89+
6190
@Test
6291
public void testNullFirst()
6392
{
@@ -85,8 +114,6 @@ public void testNullFirst()
85114
assertEquals(s.last().getValue().getName(), null);
86115
}
87116

88-
89-
90117
@Test
91118
public void testBetweenFilter()
92119
{
@@ -185,5 +212,99 @@ public void testBetweenFilter()
185212
{
186213
fail("Test run interrupted");
187214
}
215+
216+
cache.removeIndex(extrFirst);
217+
cache.removeIndex(extrLast);
218+
cache.removeIndex(extrYear);
219+
}
220+
221+
// ----- helper methods -------------------------------------------------
222+
223+
/**
224+
* Test the performance of retainAll() and removeAll() as any deviation
225+
* can result in query timeouts
226+
*
227+
* @param fRetain Whether retainAll() or removeAll() should be tested
228+
*/
229+
public void testSubset(boolean fRetain)
230+
{
231+
SubSet<Binary> setKeys = new SubSet<>(createSet());
232+
SubSet<Binary> setKeys2 = new SubSet<>(setKeys);
233+
NullableSortedMap<Integer, Set<Binary>> mapTail = new NullableSortedMap<>();
234+
for (int i = 0; i < 43000; i++)
235+
{
236+
mapTail.put(i, Collections.singleton(createBinary(i)));
237+
}
238+
239+
// new Style
240+
long start = System.currentTimeMillis();
241+
List<Set<?>> listGT = new ArrayList<>(mapTail.size());
242+
for (Object o : mapTail.values())
243+
{
244+
Set set = (Set) o;
245+
listGT.add(ensureSafeSet(set));
246+
}
247+
Collection<Object> c = new ChainedCollection<>(listGT.toArray(Set[]::new));
248+
249+
if (fRetain)
250+
{
251+
// test against passed set
252+
setKeys.retainAll(c);
253+
// test against "original" set
254+
setKeys.retainAll(c);
255+
}
256+
else
257+
{
258+
// test against passed set
259+
setKeys.removeAll(c);
260+
// test against "original" set
261+
setKeys.removeAll(c);
262+
}
263+
264+
long newStyle = System.currentTimeMillis() - start;
265+
266+
// old style:
267+
start = System.currentTimeMillis();
268+
NullableSortedMap<Integer, Set<Binary>> mapGE = new NullableSortedMap<>(mapTail);
269+
Set setGT = new HashSet();
270+
for (Iterator iterGE = mapGE.values().iterator(); iterGE.hasNext(); )
271+
{
272+
Set set = (Set) iterGE.next();
273+
setGT.addAll(ensureSafeSet(set));
274+
}
275+
if (fRetain)
276+
{
277+
setKeys2.retainAll(setGT);
278+
}
279+
else
280+
{
281+
setKeys2.removeAll(setGT);
282+
}
283+
long oldStyle = System.currentTimeMillis() - start;
284+
System.out.println("Time: new style(partitioned index):" + newStyle + " ms, old style(monolithic index):" + oldStyle + " ms");
285+
Assert.assertEquals(setKeys, setKeys2);
286+
// new style called twice ~ as good as old style called once
287+
Assert.assertTrue((newStyle * .25) <= oldStyle);
288+
}
289+
290+
private Set<Binary> createSet()
291+
{
292+
Set<Binary> s = new HashSet<>();
293+
for (int i = 0; i < 30000; i++)
294+
{
295+
s.add(createBinary(i + 15000));
296+
}
297+
return s;
298+
}
299+
300+
protected static Set ensureSafeSet(Set set)
301+
{
302+
return set == null ? Collections.emptySet() : set;
303+
}
304+
305+
private Binary createBinary(int i)
306+
{
307+
String s = "randomkey_no_test_gldfkjglkdfgjdflkgjdflkgjlkdfjglkdfjglkdfjglkdf" + i;
308+
return new Binary(s.getBytes(StandardCharsets.UTF_8));
188309
}
189310
}

0 commit comments

Comments
 (0)