Skip to content

Commit 01e8fb6

Browse files
BiryukovVAantonovsergey93
authored andcommitted
IGNITE-7986 GridPartitionStateMap.entrySet() optimization. - Fixes apache#3659.
Signed-off-by: dpavlov <dpavlov@apache.org> (cherry picked from commit bc7814e)
1 parent c492aa4 commit 01e8fb6

File tree

2 files changed

+129
-12
lines changed

2 files changed

+129
-12
lines changed

modules/core/src/main/java/org/apache/ignite/internal/util/GridPartitionStateMap.java

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,19 @@ public class GridPartitionStateMap extends AbstractMap<Integer, GridDhtPartition
4242
private static final int BITS = Integer.SIZE -
4343
Integer.numberOfLeadingZeros(GridDhtPartitionState.values().length + 1);
4444

45-
/** */
45+
/**
46+
* Contains partition map.
47+
* For a map containing 3 partitions state with a size of 3 bits storage will be done this way:
48+
* <pre>
49+
* +-----------+-----------+-----------+
50+
* | p0 - 100 | p1 - 011 | p2 - 001 |
51+
* +---+---+---+---+---+---+---+---+---+
52+
* | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 |
53+
* +---+---+---+---+---+---+---+---+---+
54+
* </pre>
55+
* The first element takes the first {@link GridPartitionStateMap#BITS} bits in reverse order,
56+
* the second element next {@link GridPartitionStateMap#BITS} bits in reverse order, etc.
57+
*/
4658
private final BitSet states;
4759

4860
/** */
@@ -52,25 +64,37 @@ public class GridPartitionStateMap extends AbstractMap<Integer, GridDhtPartition
5264
@Override public Set<Entry<Integer, GridDhtPartitionState>> entrySet() {
5365
return new AbstractSet<Entry<Integer, GridDhtPartitionState>>() {
5466
@Override public Iterator<Entry<Integer, GridDhtPartitionState>> iterator() {
55-
final int size = states.isEmpty() ? 0 : (states.length() - 1) / BITS + 1;
56-
5767
return new Iterator<Entry<Integer, GridDhtPartitionState>>() {
58-
private int next;
68+
/** Current {@link GridPartitionStateMap#states} index. */
69+
private int idx;
70+
71+
/** Current key value. */
5972
private int cur;
6073

6174
@Override public boolean hasNext() {
62-
while(state(next) == null && next < size)
63-
next++;
75+
idx = states.nextSetBit(idx);
6476

65-
return next < size;
77+
return idx != -1;
6678
}
6779

6880
@Override public Entry<Integer, GridDhtPartitionState> next() {
6981
if (!hasNext())
7082
throw new NoSuchElementException();
7183

72-
cur = next;
73-
next++;
84+
cur = idx / BITS;
85+
86+
int bitN = idx % BITS;
87+
88+
// Get state value from BitSet like in GridPartitionStateMap#state, but don't process known zero bits.
89+
int st = 1 << bitN;
90+
91+
// Accumulating values of remaining bits
92+
for (int i = 1; i < BITS - bitN; i++)
93+
st |= (states.get(idx + i) ? 1 : 0) << i + bitN;
94+
95+
final int ordinal = st - 1;
96+
97+
idx += (BITS - bitN);
7498

7599
return new Entry<Integer, GridDhtPartitionState>() {
76100
int p = cur;
@@ -80,7 +104,7 @@ public class GridPartitionStateMap extends AbstractMap<Integer, GridDhtPartition
80104
}
81105

82106
@Override public GridDhtPartitionState getValue() {
83-
return state(p);
107+
return GridDhtPartitionState.fromOrdinal(ordinal);
84108
}
85109

86110
@Override public GridDhtPartitionState setValue(GridDhtPartitionState val) {
@@ -219,4 +243,4 @@ private GridDhtPartitionState state(int part) {
219243
@Override public int hashCode() {
220244
return 31 * states.hashCode() + size;
221245
}
222-
}
246+
}

modules/core/src/test/java/org/apache/ignite/util/GridPartitionMapSelfTest.java

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@
1919

2020
import java.util.HashMap;
2121
import java.util.HashSet;
22+
import java.util.Iterator;
2223
import java.util.Map;
2324
import java.util.Set;
25+
import java.util.TreeMap;
26+
import java.util.concurrent.ThreadLocalRandom;
2427
import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionState;
2528
import org.apache.ignite.internal.util.GridPartitionStateMap;
2629
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
@@ -144,6 +147,96 @@ public void testCopyNoActive() {
144147
assertEquals(1, cp2.size());
145148
}
146149

150+
/**
151+
* Tests that entries from {@link Iterator#next()} remain unaltered.
152+
*/
153+
public void testIteratorNext() {
154+
GridPartitionStateMap map = new GridPartitionStateMap();
155+
156+
initMap(map);
157+
158+
Iterator<Map.Entry<Integer, GridDhtPartitionState>> iter = map.entrySet().iterator();
159+
160+
for (int i = 0; i < map.size() + 1; i++)
161+
assertTrue(iter.hasNext());
162+
163+
Map.Entry<Integer, GridDhtPartitionState> entry1 = iter.next();
164+
165+
for (int i = 0; i < map.size() + 1; i++)
166+
assertTrue(iter.hasNext());
167+
168+
Map.Entry<Integer, GridDhtPartitionState> entry2 = iter.next();
169+
170+
iter.remove();
171+
172+
assertNotNull(entry1.getValue());
173+
assertNotNull(entry2.getValue());
174+
175+
assertEquals(Integer.valueOf(0), entry1.getKey());
176+
assertEquals(Integer.valueOf(1), entry2.getKey());
177+
178+
assertEquals(GridDhtPartitionState.MOVING, entry1.getValue());
179+
assertEquals(GridDhtPartitionState.RENTING, entry2.getValue());
180+
}
181+
182+
/**
183+
* Tests {@link GridDhtPartitionState} compatibility with {@link TreeMap} on random operations.
184+
*/
185+
public void testOnRandomOperations() {
186+
ThreadLocalRandom rnd = ThreadLocalRandom.current();
187+
188+
Map<Integer, GridDhtPartitionState> treeMap = new TreeMap<>();
189+
Map<Integer, GridDhtPartitionState> gridMap = new GridPartitionStateMap();
190+
191+
int statesNum = GridDhtPartitionState.values().length;
192+
193+
for (int i = 0; i < 10000; i++) {
194+
Integer part = rnd.nextInt(65536);
195+
196+
GridDhtPartitionState state = GridDhtPartitionState.fromOrdinal(rnd.nextInt(statesNum));
197+
198+
int rndOperation = rnd.nextInt(9);
199+
200+
if (rndOperation <= 5) {
201+
treeMap.put(part, state);
202+
gridMap.put(part, state);
203+
}
204+
else if (rndOperation == 6) {
205+
treeMap.remove(part);
206+
gridMap.remove(part);
207+
}
208+
else if (!treeMap.isEmpty()) {
209+
int n = rnd.nextInt(0, treeMap.size());
210+
211+
Iterator<Map.Entry<Integer, GridDhtPartitionState>> iter1 = treeMap.entrySet().iterator();
212+
Iterator<Map.Entry<Integer, GridDhtPartitionState>> iter2 = gridMap.entrySet().iterator();
213+
214+
Map.Entry<Integer, GridDhtPartitionState> entry1 = iter1.next();
215+
Map.Entry<Integer, GridDhtPartitionState> entry2 = iter2.next();
216+
217+
for (int j = 1; j <= n; j++) {
218+
entry1 = iter1.next();
219+
entry2 = iter2.next();
220+
221+
assertEquals(entry1.getValue(), entry2.getValue());
222+
}
223+
224+
if (rndOperation == 7) {
225+
entry1.setValue(state);
226+
entry2.setValue(state);
227+
}
228+
else {
229+
iter1.remove();
230+
iter2.remove();
231+
}
232+
}
233+
234+
assertEquals(treeMap.size(), gridMap.size());
235+
}
236+
237+
assertEquals(treeMap, gridMap);
238+
}
239+
147240
/** */
148241
private GridPartitionStateMap initMap(GridPartitionStateMap map) {
149242
map.put(0, GridDhtPartitionState.MOVING);
@@ -159,4 +252,4 @@ private GridPartitionStateMap initMap(GridPartitionStateMap map) {
159252

160253
return map;
161254
}
162-
}
255+
}

0 commit comments

Comments
 (0)