Skip to content

Commit 2ad5984

Browse files
moste00eregon
authored andcommitted
Fixed rehash spec for both compact hashes and bucket hashes
1 parent 77047df commit 2ad5984

File tree

3 files changed

+37
-94
lines changed

3 files changed

+37
-94
lines changed

src/main/java/org/truffleruby/core/hash/library/BucketsHashStore.java

Lines changed: 8 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
import org.truffleruby.language.objects.shared.PropagateSharingNode;
4848
import org.truffleruby.language.objects.shared.SharedObjects;
4949

50-
import java.util.Arrays;
5150
import java.util.Set;
5251

5352
@ExportLibrary(value = HashStoreLibrary.class)
@@ -465,50 +464,21 @@ protected RubyArray shift(RubyHash hash,
465464

466465
@ExportMessage
467466
protected void rehash(RubyHash hash,
468-
@Cached CompareHashKeysNode compareHashKeys,
469-
@Cached HashingNodes.ToHash hashNode) {
470-
467+
@CachedLibrary("this") HashStoreLibrary hashStoreLibrary) {
471468
assert verify(hash);
472-
final Entry[] entries = this.entries;
473-
Arrays.fill(entries, null);
469+
final Entry[] newEntries = new Entry[entries.length];
474470

475471
Entry entry = this.firstInSequence;
476-
while (entry != null) {
477-
final int newHash = hashNode.execute(entry.getKey(), hash.compareByIdentity);
478-
entry.setHashed(newHash);
479-
entry.setNextInLookup(null);
472+
BucketsHashStore newStore = new BucketsHashStore(newEntries, null, null);
473+
hash.store = newStore;
474+
hash.size = 0;
480475

481-
final int index = getBucketIndex(newHash, entries.length);
482-
Entry bucketEntry = entries[index];
483-
484-
if (bucketEntry == null) {
485-
entries[index] = entry;
486-
} else {
487-
Entry previousEntry = entry;
488-
489-
int size = hash.size;
490-
do {
491-
if (compareHashKeys.execute(
492-
hash.compareByIdentity,
493-
entry.getKey(),
494-
newHash,
495-
bucketEntry.getKey(),
496-
bucketEntry.getHashed())) {
497-
removeFromSequenceChain(entry);
498-
size--;
499-
break;
500-
}
501-
previousEntry = bucketEntry;
502-
bucketEntry = bucketEntry.getNextInLookup();
503-
} while (bucketEntry != null);
504-
505-
previousEntry.setNextInLookup(entry);
506-
hash.size = size;
507-
}
476+
while (entry != null) {
477+
hashStoreLibrary.set(newStore, hash, entry.getKey(), entry.getValue(), hash.compareByIdentity);
508478
entry = entry.getNextInSequence();
509479
}
510480

511-
assert verify(hash);
481+
assert newStore.verify(hash);
512482
}
513483

514484
@TruffleBoundary

src/main/java/org/truffleruby/core/hash/library/CompactHashStore.java

Lines changed: 18 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@
1414
import static com.oracle.truffle.api.dsl.Cached.Exclusive;
1515
import static org.truffleruby.core.hash.library.HashStoreLibrary.EachEntryCallback;
1616

17-
import java.util.ArrayList;
18-
import java.util.HashMap;
19-
import java.util.List;
20-
import java.util.Map;
2117
import java.util.Set;
2218

2319
import org.truffleruby.RubyContext;
@@ -120,10 +116,6 @@ public final class CompactHashStore {
120116
static final int KEY_NOT_FOUND = -2;
121117
private static final int HASH_NOT_FOUND = KEY_NOT_FOUND;
122118

123-
// a generic "not a valid array position" value to be used by all code doing array searches for things other than
124-
// keys and hashes
125-
private static final int INVALID_ARRAY_POSITION = Integer.MIN_VALUE;
126-
127119
// In hash entries, not array positions (in general, capacities and sizes are always in entries)
128120
public static final int DEFAULT_INITIAL_CAPACITY = 8;
129121

@@ -317,54 +309,25 @@ RubyArray shift(RubyHash hash,
317309
@TruffleBoundary
318310
@ExportMessage
319311
void rehash(RubyHash hash,
320-
@Cached @Shared HashingNodes.ToHash hashFunction,
321-
@Cached CompareHashKeysNode.AssumingEqualHashes compareHashKeysNode,
322312
@Cached @Exclusive InlinedConditionProfile slotUsed,
323-
@Cached @Exclusive InlinedConditionProfile duplicateIndexEntry,
324-
@Cached @Exclusive InlinedLoopConditionProfile indexSlotUnavailable,
325313
@Cached @Exclusive InlinedLoopConditionProfile loopProfile,
314+
@CachedLibrary("this") HashStoreLibrary hashlib,
326315
@Bind("$node") Node node) {
327-
int[] oldIndex = index;
328-
this.index = new int[oldIndex.length];
329-
Map<Integer, List<Integer>> hashesToKvPositions = new HashMap<>();
316+
Object[] oldKvStore = kvStore;
317+
int oldKvStoreInsertionPos = kvStoreInsertionPos;
318+
319+
this.kvStore = new Object[oldKvStore.length];
320+
this.kvStoreInsertionPos = 0;
321+
this.index = new int[index.length];
322+
hash.size = 0;
330323

331324
int i = 0;
332325
try {
333-
int numDuplicates = 0;
334-
for (; loopProfile.inject(node, i < oldIndex.length); i += 2) {
335-
int kvOffset = oldIndex[i + 1];
336-
337-
if (slotUsed.profile(node, kvOffset > INDEX_SLOT_UNUSED)) {
338-
Object key = kvStore[kvOffset - 1];
339-
Integer newHash = hashFunction.execute(key, hash.compareByIdentity);
340-
341-
List<Integer> kvPositions = hashesToKvPositions.get(newHash);
342-
if (kvPositions != null) {
343-
boolean duplicate = false;
344-
for (int kvPos : kvPositions) {
345-
Object possiblyEqualKey = kvStore[kvPos];
346-
if (compareHashKeysNode.execute(hash.compareByIdentity, key, possiblyEqualKey)) {
347-
duplicate = true;
348-
break;
349-
}
350-
}
351-
if (duplicateIndexEntry.profile(node, duplicate)) {
352-
numDuplicates++;
353-
kvStore[kvOffset - 1] = null;
354-
kvStore[kvOffset] = null;
355-
continue;
356-
}
357-
358-
} else {
359-
hashesToKvPositions.put(newHash, new ArrayList<>());
360-
}
361-
362-
SetKvAtNode.insertIntoIndex(newHash, kvOffset, index,
363-
indexSlotUnavailable, node);
364-
hashesToKvPositions.get(newHash).add(kvOffset - 1);
326+
for (; loopProfile.inject(node, i < oldKvStoreInsertionPos); i += 2) {
327+
if (slotUsed.profile(node, oldKvStore[i] != null)) {
328+
hashlib.set(this, hash, oldKvStore[i], oldKvStore[i + 1], hash.compareByIdentity);
365329
}
366330
}
367-
hash.size -= numDuplicates;
368331
} finally {
369332
RubyBaseNode.profileAndReportLoopCount(node, loopProfile, i >> 1);
370333
}
@@ -527,22 +490,22 @@ abstract static class GetHashNextPosInIndexNode extends RubyBaseNode {
527490

528491
@Specialization
529492
int getHashNextPos(int startingFromPos, int hash, int[] index, int stop,
530-
@Cached @Exclusive InlinedConditionProfile slotIsDeleted,
493+
@Cached @Exclusive InlinedConditionProfile slotIsNotDeleted,
531494
@Cached @Exclusive InlinedConditionProfile slotIsUnused,
532495
@Cached @Exclusive InlinedConditionProfile hashFound,
533-
@Cached @Exclusive InlinedConditionProfile noValidFirstDeletedSlot,
534496
@Cached @Exclusive InlinedLoopConditionProfile stopNotYetReached,
535497
@Bind("$node") Node node) {
536498
int nextHashPos = startingFromPos;
499+
537500
do {
538501
if (slotIsUnused.profile(node, index[nextHashPos + 1] == INDEX_SLOT_UNUSED)) {
539502
return HASH_NOT_FOUND;
540503
}
541504

542-
if (slotIsDeleted.profile(node, index[nextHashPos + 1] == INDEX_SLOT_DELETED)) {
543-
// next
544-
} else if (hashFound.profile(node, index[nextHashPos] == hash)) {
545-
return nextHashPos;
505+
if (slotIsNotDeleted.profile(node, index[nextHashPos + 1] != INDEX_SLOT_DELETED)) {
506+
if (hashFound.profile(node, index[nextHashPos] == hash)) {
507+
return nextHashPos;
508+
}
546509
}
547510

548511
nextHashPos = incrementIndexPos(nextHashPos, index.length);
@@ -595,7 +558,7 @@ boolean keyAlreadyExistsWithDifferentValue(
595558
return false;
596559
}
597560

598-
// setting the key is a relatively expensive insertion
561+
// setting a new key is a relatively expensive insertion
599562
@Specialization(guards = "kvPos == KEY_NOT_FOUND")
600563
static boolean keyDoesntExist(
601564
RubyHash hash, CompactHashStore store, int kvPos, int keyHash, Object frozenKey, Object value,

src/main/java/org/truffleruby/core/hash/library/PackedHashStoreLibrary.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ public final class PackedHashStoreLibrary {
6363
private static final int ELEMENTS_PER_ENTRY = 3;
6464
public static final int TOTAL_ELEMENTS = MAX_ENTRIES * ELEMENTS_PER_ENTRY;
6565

66+
private static final boolean bigHashTypeIsCompact;
67+
static {
68+
String hashType = System.getProperty("BigHashStrategy");
69+
bigHashTypeIsCompact = hashType != null && hashType.equalsIgnoreCase("compact");
70+
}
71+
6672
// region Utilities
6773

6874
public static Object[] createStore() {
@@ -235,7 +241,11 @@ static boolean set(Object[] store, RubyHash hash, Object key, Object value, bool
235241
return true;
236242
}
237243

238-
promoteToCompact(hash, store, size);
244+
if (bigHashTypeIsCompact) {
245+
promoteToCompact(hash, store, size);
246+
} else {
247+
promoteToBuckets(hash, store, size);
248+
}
239249
hashes.set(hash.store, hash, key2, value, byIdentity);
240250
return true;
241251
}

0 commit comments

Comments
 (0)