Skip to content

Commit fd06e7c

Browse files
committed
ObjectHashMap: move remove into a node
1 parent 2000d28 commit fd06e7c

File tree

3 files changed

+80
-62
lines changed

3 files changed

+80
-62
lines changed

graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/objects/ObjectHashMapTests.java

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,6 @@ public boolean execute(Frame frame, Object a, Object b) {
9393
}
9494
}
9595

96-
private static final ObjectHashMap.PutProfiles PUT_PROFILES = new PutProfiles(false, new EqNodeStub());
97-
private static final ObjectHashMap.GetProfiles GET_PROFILES = new GetProfiles(false, new EqNodeStub());
98-
private static final ObjectHashMap.RemoveProfiles RM_PROFILES = new RemoveProfiles(false, new EqNodeStub());
99-
10096
@Test
10197
public void testCollisionsByPuttingManyKeysWithSameHash() {
10298
ObjectHashMap map = new ObjectHashMap();
@@ -111,7 +107,7 @@ public void testCollisionsByPuttingManyKeysWithSameHash() {
111107
for (int i = 0; i < 55; i++) {
112108
DictKey key = expected.keySet().stream().skip(22).findFirst().get();
113109
expected.remove(key);
114-
map.remove(null, key, 42, RM_PROFILES);
110+
remove(map, key, 42);
115111
assertEqual(i, expected, map);
116112
}
117113
for (int i = 0; i < 10; i++) {
@@ -134,7 +130,7 @@ public void testCollisionsByPuttingAndRemovingTheSameKey() {
134130
expected.put(key, value);
135131
assertEqual(i, expected, map);
136132

137-
map.remove(null, key, 42, RM_PROFILES);
133+
remove(map, key, 42);
138134
expected.remove(key);
139135
assertEqual(i, expected, map);
140136
}
@@ -153,7 +149,7 @@ public void testCollisionsByPuttingAndRemovingTheSameKeys() {
153149
assertEqual(i, expected, map);
154150

155151
final DictKey toRemove = keys[(i + 1) % keys.length];
156-
map.remove(null, toRemove, toRemove.hash, RM_PROFILES);
152+
remove(map, toRemove, toRemove.hash);
157153
expected.remove(toRemove);
158154
assertEqual(i, expected, map);
159155
}
@@ -215,14 +211,14 @@ private static void removeAll(ObjectHashMap map) {
215211
keys.add((Long) key);
216212
}
217213
for (Long key : keys) {
218-
map.remove(null, key, PyObjectHashNode.hash(key), RM_PROFILES);
214+
remove(map, key, PyObjectHashNode.hash(key));
219215
assertNull(get(map, key, PyObjectHashNode.hash(key)));
220216
}
221217
}
222218

223219
private static void removeAll(ObjectHashMap map, LinkedHashMap<Long, Object> expected) {
224220
for (Long key : expected.keySet().toArray(new Long[0])) {
225-
map.remove(null, key, PyObjectHashNode.hash(key), RM_PROFILES);
221+
remove(map, key, PyObjectHashNode.hash(key));
226222
expected.remove(key);
227223
assertEqual(Long.toString(key), expected, map);
228224
}
@@ -232,7 +228,7 @@ private static void removeValues(ObjectHashMap map, LinkedHashMap<Long, Object>
232228
for (int i = 0; i < count; i++) {
233229
int index = rand.nextInt(expected.size() - 1);
234230
long key = expected.keySet().stream().skip(index).findFirst().get();
235-
map.remove(null, key, PyObjectHashNode.hash(key), RM_PROFILES);
231+
remove(map, key, PyObjectHashNode.hash(key));
236232
expected.remove(key);
237233
assertEqual(i, expected, map);
238234
}
@@ -327,6 +323,13 @@ private static Object get(ObjectHashMap map, Object key, long hash) {
327323
new EqNodeStub());
328324
}
329325

326+
private static void remove(ObjectHashMap map, Object key, long hash) {
327+
ObjectHashMap.RemoveNode.doRemove(null, map, key, hash,//
328+
ConditionProfile.getUncached(), ConditionProfile.getUncached(), ConditionProfile.getUncached(),//
329+
ConditionProfile.getUncached(), BranchProfile.getUncached(), ConditionProfile.getUncached(),//
330+
new EqNodeStub());
331+
}
332+
330333
private static void put(ObjectHashMap map, Object key, long hash, Object value) {
331334
ObjectHashMap.PutNode.doPut(null, map, key, hash, value,//
332335
ConditionProfile.getUncached(), ConditionProfile.getUncached(), ConditionProfile.getUncached(),//

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/EconomicMapStorage.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,11 +300,11 @@ static HashingStorage generic(EconomicMapStorage self, HashingStorage other,
300300

301301
@ExportMessage
302302
HashingStorage delItemWithState(Object key, ThreadState state,
303-
@Cached ObjectHashMap.RemoveProfiles profiles,
303+
@Cached ObjectHashMap.RemoveNode removeNode,
304304
@Shared("hashNode") @Cached PyObjectHashNode hashNode,
305305
@Shared("gotState") @Cached ConditionProfile gotState) {
306306
VirtualFrame frame = gotState.profile(state == null) ? null : PArguments.frameForCall(state);
307-
map.remove(frame, key, hashNode.execute(frame, key), profiles);
307+
removeNode.remove(state, map, key, hashNode.execute(frame, key));
308308
return this;
309309
}
310310

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/ObjectHashMap.java

Lines changed: 65 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -702,63 +702,78 @@ private boolean needsCompaction() {
702702
return dummyCnt > quarterOfUsable;
703703
}
704704

705-
public void remove(VirtualFrame frame, Object key, long keyHash, RemoveProfiles profiles) {
706-
assert checkInternalState();
707-
if (CompilerDirectives.injectBranchProbability(SLOWPATH_PROBABILITY, needsCompaction())) {
708-
profiles.compactProfile.enter();
709-
compact();
710-
}
711-
// Note: CPython is not shrinking the capacity of the hash table on delete, we do the same
712-
int compactIndex = getIndex(keyHash);
713-
int index = indices[compactIndex];
714-
if (profiles.foundNullKey.profile(index == EMPTY_INDEX)) {
715-
return; // not found
705+
706+
@GenerateUncached
707+
public abstract static class RemoveNode extends Node {
708+
public final void remove(ThreadState state, ObjectHashMap map, DictKey key) {
709+
execute(state, map, key.getValue(), key.getPythonHash());
716710
}
717711

718-
int unwrappedIndex = unwrapIndex(index);
719-
if (profiles.foundEqKey.profile(index != DUMMY_INDEX && keysEqual(frame, unwrappedIndex, key, keyHash, profiles))) {
720-
indices[compactIndex] = DUMMY_INDEX;
721-
setValue(unwrappedIndex, null);
722-
setKey(unwrappedIndex, null);
723-
size--;
724-
return;
712+
public final void remove(ThreadState state, ObjectHashMap map, Object key, long keyHash) {
713+
execute(state, map, key, keyHash);
725714
}
726715

727-
// collision: intentionally counted loop
728-
long perturb = keyHash;
729-
int searchLimit = getBucketsCount() + PERTURB_SHIFTS_COUT;
730-
int i = 0;
731-
try {
732-
for (; CompilerDirectives.injectBranchProbability(0, i < searchLimit); i++) {
733-
perturb >>>= PERTURB_SHIFT;
734-
compactIndex = nextIndex(compactIndex, perturb);
735-
index = indices[compactIndex];
736-
if (profiles.collisionFoundNoValue.profile(index == EMPTY_INDEX)) {
737-
return; // not found
738-
}
739-
unwrappedIndex = unwrapIndex(index);
740-
if (profiles.collisionFoundEqKey.profile(index != DUMMY_INDEX && keysEqual(frame, unwrappedIndex, key, keyHash, profiles))) {
741-
indices[compactIndex] = DUMMY_INDEX;
742-
setValue(unwrappedIndex, null);
743-
setKey(unwrappedIndex, null);
744-
size--;
745-
return;
746-
}
716+
abstract void execute(ThreadState state, ObjectHashMap map, Object key, long keyHash);
717+
718+
// "public" for testing...
719+
@Specialization
720+
public static void doRemove(ThreadState state, ObjectHashMap map, Object key, long keyHash,
721+
@Cached("createCountingProfile()") ConditionProfile foundNullKey,
722+
@Cached("createCountingProfile()") ConditionProfile foundEqKey,
723+
@Cached("createCountingProfile()") ConditionProfile collisionFoundNoValue,
724+
@Cached("createCountingProfile()") ConditionProfile collisionFoundEqKey,
725+
@Cached BranchProfile compactProfile,
726+
@Cached ConditionProfile hasState,
727+
@Cached PyObjectRichCompareBool.EqNode eqNode) {
728+
assert map.checkInternalState();
729+
if (CompilerDirectives.injectBranchProbability(SLOWPATH_PROBABILITY, map.needsCompaction())) {
730+
compactProfile.enter();
731+
map.compact();
732+
}
733+
// Note: CPython is not shrinking the capacity of the hash table on delete, we do the same
734+
int compactIndex = map.getIndex(keyHash);
735+
int index = map.indices[compactIndex];
736+
if (foundNullKey.profile(index == EMPTY_INDEX)) {
737+
return; // not found
747738
}
748-
} finally {
749-
LoopNode.reportLoopCount(profiles, i);
750-
}
751-
// all values are dummies? Not possible, since we should have compacted the
752-
// hashes/keysAndValues arrays at the top
753-
throw CompilerDirectives.shouldNotReachHere();
754-
}
755739

756-
private boolean keysEqual(Frame frame, int index, Object key, long keyHash, GetProfiles profiles) {
757-
return hashes[index] == keyHash && profiles.eqNode.execute(frame, getKey(index), key);
758-
}
740+
int unwrappedIndex = unwrapIndex(index);
741+
if (foundEqKey.profile(index != DUMMY_INDEX && map.keysEqual(state, unwrappedIndex, key, keyHash, eqNode, hasState))) {
742+
map.indices[compactIndex] = DUMMY_INDEX;
743+
map.setValue(unwrappedIndex, null);
744+
map.setKey(unwrappedIndex, null);
745+
map.size--;
746+
return;
747+
}
759748

760-
private boolean keysEqual(Frame frame, int index, Object key, long keyHash, PyObjectRichCompareBool.EqNode eqNode) {
761-
return hashes[index] == keyHash && eqNode.execute(frame, getKey(index), key);
749+
// collision: intentionally counted loop
750+
long perturb = keyHash;
751+
int searchLimit = map.getBucketsCount() + PERTURB_SHIFTS_COUT;
752+
int i = 0;
753+
try {
754+
for (; CompilerDirectives.injectBranchProbability(0, i < searchLimit); i++) {
755+
perturb >>>= PERTURB_SHIFT;
756+
compactIndex = map.nextIndex(compactIndex, perturb);
757+
index = map.indices[compactIndex];
758+
if (collisionFoundNoValue.profile(index == EMPTY_INDEX)) {
759+
return; // not found
760+
}
761+
unwrappedIndex = unwrapIndex(index);
762+
if (collisionFoundEqKey.profile(index != DUMMY_INDEX && map.keysEqual(state, unwrappedIndex, key, keyHash, eqNode, hasState))) {
763+
map.indices[compactIndex] = DUMMY_INDEX;
764+
map.setValue(unwrappedIndex, null);
765+
map.setKey(unwrappedIndex, null);
766+
map.size--;
767+
return;
768+
}
769+
}
770+
} finally {
771+
LoopNode.reportLoopCount(eqNode, i);
772+
}
773+
// all values are dummies? Not possible, since we should have compacted the
774+
// hashes/keysAndValues arrays at the top
775+
throw CompilerDirectives.shouldNotReachHere();
776+
}
762777
}
763778

764779
private boolean keysEqual(ThreadState state, int index, Object key, long keyHash, PyObjectRichCompareBool.EqNode eqNode, ConditionProfile hasState) {

0 commit comments

Comments
 (0)