Skip to content

Commit 630660c

Browse files
committed
[Truffle] Pull out array packing logic for hashes.
1 parent a9e1b0b commit 630660c

File tree

6 files changed

+82
-48
lines changed

6 files changed

+82
-48
lines changed

core/src/main/java/org/jruby/util/cli/Options.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public class Options {
134134
public static final Option<Integer> TRUFFLE_ARRAYS_UNINITIALIZED_SIZE = integer(TRUFFLE, "truffle.arrays.uninitialized_size", 32, "How large an array to allocate when we have no other information to go on.");
135135
public static final Option<Boolean> TRUFFLE_ARRAYS_OPTIMISTIC_LONG = bool(TRUFFLE, "truffle.arrays.optimistic.long", true, "If we allocate an int[] for an Array and it has been converted to a long[], directly allocate a long[] next time.");
136136
public static final Option<Integer> TRUFFLE_ARRAYS_SMALL = integer(TRUFFLE, "truffle.arrays.small", 3, "Maximum size of an Array to consider small for optimisations.");
137-
public static final Option<Integer> TRUFFLE_HASHES_SMALL = integer(TRUFFLE, "truffle.hashes.small", 3, "Maximum size of a Hash to consider small for optimisations.");
137+
public static final Option<Integer> TRUFFLE_HASH_PACKED_ARRAY_MAX = integer(TRUFFLE, "truffle.hash.packed_array_max", 3, "Maximum size of a Hash to use with the packed array storage strategy.");
138138

139139
public static final Option<Boolean> TRUFFLE_LOAD_CORE = bool(TRUFFLE, "truffle.load_core", true, "Load the Truffle core library.");
140140

truffle/src/main/java/org/jruby/truffle/nodes/core/HashNodes.java

Lines changed: 36 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,7 @@
2929
import org.jruby.truffle.runtime.UndefinedPlaceholder;
3030
import org.jruby.truffle.runtime.control.RaiseException;
3131
import org.jruby.truffle.runtime.core.*;
32-
import org.jruby.truffle.runtime.hash.Entry;
33-
import org.jruby.truffle.runtime.hash.HashOperations;
34-
import org.jruby.truffle.runtime.hash.HashSearchResult;
35-
import org.jruby.truffle.runtime.hash.KeyValue;
32+
import org.jruby.truffle.runtime.hash.*;
3633
import org.jruby.truffle.runtime.methods.InternalMethod;
3734

3835
import java.util.Arrays;
@@ -58,9 +55,9 @@ public Object construct(VirtualFrame frame, RubyClass hashClass, Object[] args)
5855
final Object[] store = (Object[]) array.getStore();
5956

6057
final int size = array.getSize();
61-
final Object[] newStore = new Object[HashOperations.SMALL_HASH_SIZE * 2];
58+
final Object[] newStore = new Object[PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX * 2];
6259

63-
for (int n = 0; n < HashOperations.SMALL_HASH_SIZE; n++) {
60+
for (int n = 0; n < PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX; n++) {
6461
if (n < size) {
6562
final Object pair = store[n];
6663

@@ -77,9 +74,7 @@ public Object construct(VirtualFrame frame, RubyClass hashClass, Object[] args)
7774
}
7875

7976
final Object[] pairStore = (Object[]) pairArray.getStore();
80-
81-
newStore[n * 2] = pairStore[0];
82-
newStore[n * 2 + 1] = pairStore[1];
77+
PackedArrayStrategy.setKeyValue(newStore, n, pairStore[0], pairStore[1]);
8378
}
8479
}
8580

@@ -110,7 +105,7 @@ public static boolean isSmallArrayOfPairs(RubyClass hashClass, Object[] args) {
110105

111106
final Object[] store = (Object[]) array.getStore();
112107

113-
if (store.length > HashOperations.SMALL_HASH_SIZE) {
108+
if (store.length > PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX) {
114109
return false;
115110
}
116111

@@ -165,18 +160,18 @@ public Object getPackedArray(VirtualFrame frame, RubyHash hash, Object key) {
165160
final Object[] store = (Object[]) hash.getStore();
166161
final int size = hash.getSize();
167162

168-
for (int n = 0; n < HashOperations.SMALL_HASH_SIZE; n++) {
163+
for (int n = 0; n < PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX; n++) {
169164
if (n < size) {
170165
final boolean equal;
171166

172167
if (byIdentityProfile.profile(hash.isCompareByIdentity())) {
173-
equal = equalNode.executeReferenceEqual(frame, key, store[n * 2]);
168+
equal = equalNode.executeReferenceEqual(frame, key, PackedArrayStrategy.getKey(store, n));
174169
} else {
175-
equal = eqlNode.callBoolean(frame, key, "eql?", null, store[n * 2]);
170+
equal = eqlNode.callBoolean(frame, key, "eql?", null, PackedArrayStrategy.getKey(store, n));
176171
}
177172

178173
if (equal) {
179-
return store[n * 2 + 1];
174+
return PackedArrayStrategy.getValue(store, n);
180175
}
181176
}
182177
}
@@ -255,7 +250,7 @@ public SetIndexNode(RubyContext context, SourceSection sourceSection) {
255250

256251
@Specialization(guards = { "isNullStorage(hash)", "!isRubyString(key)" })
257252
public Object setNull(VirtualFrame frame, RubyHash hash, Object key, Object value) {
258-
final Object[] store = new Object[HashOperations.SMALL_HASH_SIZE * 2];
253+
final Object[] store = new Object[PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX * 2];
259254
hashNode.call(frame, key, "hash", null);
260255
store[0] = key;
261256
store[1] = value;
@@ -281,18 +276,18 @@ public Object setPackedArray(VirtualFrame frame, RubyHash hash, Object key, Obje
281276
final Object[] store = (Object[]) hash.getStore();
282277
final int size = hash.getSize();
283278

284-
for (int n = 0; n < HashOperations.SMALL_HASH_SIZE; n++) {
279+
for (int n = 0; n < PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX; n++) {
285280
if (n < size) {
286281
final boolean equal;
287282

288283
if (byIdentityProfile.profile(hash.isCompareByIdentity())) {
289-
equal = equalNode.executeReferenceEqual(frame, key, store[n * 2]);
284+
equal = equalNode.executeReferenceEqual(frame, key, PackedArrayStrategy.getKey(store, n));
290285
} else {
291-
equal = eqlNode.callBoolean(frame, key, "eql?", null, store[n * 2]);
286+
equal = eqlNode.callBoolean(frame, key, "eql?", null, PackedArrayStrategy.getKey(store, n));
292287
}
293288

294289
if (equal) {
295-
store[n * 2 + 1] = value;
290+
PackedArrayStrategy.setValue(store, n, value);
296291
return value;
297292
}
298293
}
@@ -302,7 +297,7 @@ public Object setPackedArray(VirtualFrame frame, RubyHash hash, Object key, Obje
302297

303298
final int newSize = size + 1;
304299

305-
if (newSize <= HashOperations.SMALL_HASH_SIZE) {
300+
if (newSize <= PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX) {
306301
extendProfile.enter();
307302
store[size * 2] = key;
308303
store[size * 2 + 1] = value;
@@ -459,9 +454,9 @@ public Object deletePackedArray(VirtualFrame frame, RubyHash hash, Object key, O
459454
final Object[] store = (Object[]) hash.getStore();
460455
final int size = hash.getSize();
461456

462-
for (int n = 0; n < HashOperations.SMALL_HASH_SIZE; n++) {
463-
if (n < size && eqlNode.callBoolean(frame, store[n * 2], "eql?", null, key)) {
464-
final Object value = store[n * 2 + 1];
457+
for (int n = 0; n < PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX; n++) {
458+
if (n < size && eqlNode.callBoolean(frame, PackedArrayStrategy.getKey(store, n), "eql?", null, key)) {
459+
final Object value = PackedArrayStrategy.getValue(store, n);
465460

466461
// Move the later values down
467462
int k = n * 2; // position of the key
@@ -549,13 +544,13 @@ public RubyHash eachPackedArray(VirtualFrame frame, RubyHash hash, RubyProc bloc
549544
int count = 0;
550545

551546
try {
552-
for (int n = 0; n < HashOperations.SMALL_HASH_SIZE; n++) {
547+
for (int n = 0; n < PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX; n++) {
553548
if (CompilerDirectives.inInterpreter()) {
554549
count++;
555550
}
556551

557552
if (n < size) {
558-
yield(frame, block, new RubyArray(getContext().getCoreLibrary().getArrayClass(), new Object[]{store[n * 2], store[n * 2 + 1]}, 2));
553+
yield(frame, block, new RubyArray(getContext().getCoreLibrary().getArrayClass(), new Object[]{PackedArrayStrategy.getKey(store, n), PackedArrayStrategy.getValue(store, n)}, 2));
559554
}
560555
}
561556
} finally {
@@ -688,7 +683,7 @@ public RubyHash dupPackedArray(RubyHash self, RubyHash from) {
688683
}
689684

690685
final Object[] store = (Object[]) from.getStore();
691-
self.setStore(Arrays.copyOf(store, HashOperations.SMALL_HASH_SIZE * 2), from.getSize(), null, null);
686+
self.setStore(Arrays.copyOf(store, PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX * 2), from.getSize(), null, null);
692687

693688
copyOther(self, from);
694689

@@ -747,10 +742,10 @@ public RubyArray mapPackedArray(VirtualFrame frame, RubyHash hash, RubyProc bloc
747742
int count = 0;
748743

749744
try {
750-
for (int n = 0; n < HashOperations.SMALL_HASH_SIZE; n++) {
745+
for (int n = 0; n < PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX; n++) {
751746
if (n < size) {
752-
final Object key = store[n * 2];
753-
final Object value = store[n * 2 + 1];
747+
final Object key = PackedArrayStrategy.getKey(store, n);
748+
final Object value = PackedArrayStrategy.getValue(store, n);
754749
result[n] = yield(frame, block, key, value);
755750

756751
if (CompilerDirectives.inInterpreter()) {
@@ -795,7 +790,7 @@ public abstract static class MergeNode extends YieldingCoreMethodNode {
795790
private final BranchProfile considerResultIsSmallProfile = BranchProfile.create();
796791
private final BranchProfile resultIsSmallProfile = BranchProfile.create();
797792

798-
private final int smallHashSize = HashOperations.SMALL_HASH_SIZE;
793+
private final int smallHashSize = PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX;
799794

800795
public MergeNode(RubyContext context, SourceSection sourceSection) {
801796
super(context, sourceSection);
@@ -805,7 +800,7 @@ public MergeNode(RubyContext context, SourceSection sourceSection) {
805800
@Specialization(guards = {"isPackedArrayStorage(hash)", "isNullStorage(other)", "!isCompareByIdentity(hash)"})
806801
public RubyHash mergePackedArrayNull(RubyHash hash, RubyHash other, UndefinedPlaceholder block) {
807802
final Object[] store = (Object[]) hash.getStore();
808-
final Object[] copy = Arrays.copyOf(store, HashOperations.SMALL_HASH_SIZE * 2);
803+
final Object[] copy = Arrays.copyOf(store, PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX * 2);
809804

810805
return new RubyHash(hash.getLogicalClass(), hash.getDefaultBlock(), hash.getDefaultValue(), copy, hash.getSize(), null);
811806
}
@@ -826,11 +821,11 @@ public RubyHash mergePackedArrayPackedArray(VirtualFrame frame, RubyHash hash, R
826821

827822
int conflictsCount = 0;
828823

829-
for (int a = 0; a < HashOperations.SMALL_HASH_SIZE; a++) {
824+
for (int a = 0; a < PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX; a++) {
830825
if (a < storeASize) {
831826
boolean merge = true;
832827

833-
for (int b = 0; b < HashOperations.SMALL_HASH_SIZE; b++) {
828+
for (int b = 0; b < PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX; b++) {
834829
if (b < storeBSize) {
835830
if (eqlNode.callBoolean(frame, storeA[a * 2], "eql?", null, storeB[b * 2])) {
836831
conflictsCount++;
@@ -850,14 +845,14 @@ public RubyHash mergePackedArrayPackedArray(VirtualFrame frame, RubyHash hash, R
850845

851846
if (mergeFromACount == 0) {
852847
nothingFromFirstProfile.enter();
853-
return new RubyHash(hash.getLogicalClass(), hash.getDefaultBlock(), hash.getDefaultValue(), Arrays.copyOf(storeB, HashOperations.SMALL_HASH_SIZE * 2), storeBSize, null);
848+
return new RubyHash(hash.getLogicalClass(), hash.getDefaultBlock(), hash.getDefaultValue(), Arrays.copyOf(storeB, PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX * 2), storeBSize, null);
854849
}
855850

856851
considerNothingFromSecondProfile.enter();
857852

858853
if (conflictsCount == storeBSize) {
859854
nothingFromSecondProfile.enter();
860-
return new RubyHash(hash.getLogicalClass(), hash.getDefaultBlock(), hash.getDefaultValue(), Arrays.copyOf(storeA, HashOperations.SMALL_HASH_SIZE * 2), storeASize, null);
855+
return new RubyHash(hash.getLogicalClass(), hash.getDefaultBlock(), hash.getDefaultValue(), Arrays.copyOf(storeA, PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX * 2), storeASize, null);
861856
}
862857

863858
considerResultIsSmallProfile.enter();
@@ -867,21 +862,21 @@ public RubyHash mergePackedArrayPackedArray(VirtualFrame frame, RubyHash hash, R
867862
if (storeBSize + mergeFromACount <= smallHashSize) {
868863
resultIsSmallProfile.enter();
869864

870-
final Object[] merged = new Object[HashOperations.SMALL_HASH_SIZE * 2];
865+
final Object[] merged = new Object[PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX * 2];
871866

872867
int index = 0;
873868

874869
for (int n = 0; n < storeASize; n++) {
875870
if (mergeFromA[n]) {
876-
merged[index] = storeA[n * 2];
877-
merged[index + 1] = storeA[n * 2 + 1];
871+
merged[index] = PackedArrayStrategy.getKey(storeA, n);
872+
merged[index + 1] = PackedArrayStrategy.getValue(storeA, n);
878873
index += 2;
879874
}
880875
}
881876

882877
for (int n = 0; n < storeBSize; n++) {
883-
merged[index] = storeB[n * 2];
884-
merged[index + 1] = storeB[n * 2 + 1];
878+
merged[index] = PackedArrayStrategy.getKey(storeB, n);
879+
merged[index + 1] = PackedArrayStrategy.getValue(storeB, n);
885880
index += 2;
886881
}
887882

@@ -1026,7 +1021,7 @@ public RubyArray shiftPackedArray(RubyHash hash) {
10261021
final Object key = store[0];
10271022
final Object value = store[1];
10281023

1029-
System.arraycopy(store, 2, store, 0, HashOperations.SMALL_HASH_SIZE * 2 - 2);
1024+
System.arraycopy(store, 2, store, 0, PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX * 2 - 2);
10301025

10311026
hash.setSize(hash.getSize() - 1);
10321027

truffle/src/main/java/org/jruby/truffle/nodes/literal/HashLiteralNode.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.jruby.truffle.runtime.core.RubyString;
2525
import org.jruby.truffle.runtime.hash.HashOperations;
2626
import org.jruby.truffle.runtime.hash.KeyValue;
27+
import org.jruby.truffle.runtime.hash.PackedArrayStrategy;
2728

2829
import java.util.ArrayList;
2930
import java.util.List;
@@ -57,7 +58,7 @@ public RubyNode getValue(int index) {
5758
public static HashLiteralNode create(RubyContext context, SourceSection sourceSection, RubyNode[] keyValues) {
5859
if (keyValues.length == 0) {
5960
return new EmptyHashLiteralNode(context, sourceSection);
60-
} else if (keyValues.length <= HashOperations.SMALL_HASH_SIZE * 2) {
61+
} else if (keyValues.length <= PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX * 2) {
6162
return new SmallHashLiteralNode(context, sourceSection, keyValues);
6263
} else {
6364
return new GenericHashLiteralNode(context, sourceSection, keyValues);
@@ -108,7 +109,7 @@ public SmallHashLiteralNode(RubyContext context, SourceSection sourceSection, Ru
108109
@ExplodeLoop
109110
@Override
110111
public RubyHash executeRubyHash(VirtualFrame frame) {
111-
final Object[] storage = new Object[HashOperations.SMALL_HASH_SIZE * 2];
112+
final Object[] storage = new Object[PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX * 2];
112113

113114
int end = 0;
114115

truffle/src/main/java/org/jruby/truffle/runtime/core/RubyHash.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.jruby.truffle.runtime.hash.Entry;
1717
import org.jruby.truffle.runtime.hash.HashOperations;
1818
import org.jruby.truffle.runtime.hash.KeyValue;
19+
import org.jruby.truffle.runtime.hash.PackedArrayStrategy;
1920
import org.jruby.truffle.runtime.subsystems.ObjectSpaceManager;
2021

2122
public class RubyHash extends RubyBasicObject {
@@ -111,11 +112,11 @@ public boolean verifyStore(Object store, int storeSize, Entry firstInSequence, E
111112
assert firstInSequence == null || foundFirst;
112113
assert lastInSequence == null || foundLast;
113114
} else if (store instanceof Object[]) {
114-
assert ((Object[]) store).length == HashOperations.SMALL_HASH_SIZE * 2 : ((Object[]) store).length;
115+
assert ((Object[]) store).length == PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX * 2 : ((Object[]) store).length;
115116

116117
final Object[] packedStore = (Object[]) store;
117118

118-
for (int n = 0; n < HashOperations.SMALL_HASH_SIZE; n++) {
119+
for (int n = 0; n < PackedArrayStrategy.TRUFFLE_HASH_PACKED_ARRAY_MAX; n++) {
119120
if (n < storeSize) {
120121
assert packedStore[n * 2] != null;
121122
assert packedStore[n * 2 + 1] != null;

truffle/src/main/java/org/jruby/truffle/runtime/hash/HashOperations.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424

2525
public class HashOperations {
2626

27-
public static final int SMALL_HASH_SIZE = Options.TRUFFLE_HASHES_SMALL.load();
28-
2927
private static final int[] CAPACITIES = Arrays.copyOf(org.jruby.RubyHash.MRI_PRIMES, org.jruby.RubyHash.MRI_PRIMES.length - 1);
3028
private static final int SIGN_BIT_MASK = ~(1 << 31);
3129

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
3+
* code is released under a tri EPL/GPL/LGPL license. You can use it,
4+
* redistribute it and/or modify it under the terms of the:
5+
*
6+
* Eclipse Public License version 1.0
7+
* GNU General Public License version 2
8+
* GNU Lesser General Public License version 2.1
9+
*/
10+
package org.jruby.truffle.runtime.hash;
11+
12+
import org.jruby.util.cli.Options;
13+
14+
public abstract class PackedArrayStrategy {
15+
16+
public static final int TRUFFLE_HASH_PACKED_ARRAY_MAX = Options.TRUFFLE_HASH_PACKED_ARRAY_MAX.load();
17+
18+
public static Object getKey(Object[] store, int n) {
19+
return store[n * 2];
20+
}
21+
22+
public static Object getValue(Object[] store, int n) {
23+
return store[n * 2 + 1];
24+
}
25+
26+
public static void setKey(Object[] store, int n, Object key) {
27+
store[n * 2] = key;
28+
}
29+
30+
public static void setValue(Object[] store, int n, Object value) {
31+
store[n * 2 + 1] = value;
32+
}
33+
34+
public static void setKeyValue(Object[] store, int n, Object key, Object value) {
35+
setKey(store, n, key);
36+
setValue(store, n, value);
37+
}
38+
39+
}

0 commit comments

Comments
 (0)