Skip to content

Commit

Permalink
Use custom map implementation for chunks.
Browse files Browse the repository at this point in the history
This reduces startup time by ~30% (e.g., from 1050ms to ~725ms).
  • Loading branch information
fniephaus committed Oct 13, 2024
1 parent 8d7e089 commit 0ae803a
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,9 @@ public SqueakImageChunk getClassChunk() {
final int classIndex = getClassIndex();
final int majorIndex = SqueakImageConstants.majorClassIndexOf(classIndex);
final int minorIndex = SqueakImageConstants.minorClassIndexOf(classIndex);
final SqueakImageChunk classTablePage = reader.getChunk(reader.hiddenRootsChunk.getWord(majorIndex));
final SqueakImageChunk classTablePage = reader.chunkMap.get(reader.hiddenRootsChunk.getWord(majorIndex));
assert !classTablePage.isNil() : "Class page does not exist";
final SqueakImageChunk classChunk = reader.getChunk(classTablePage.getWord(minorIndex));
final SqueakImageChunk classChunk = reader.chunkMap.get(classTablePage.getWord(minorIndex));
assert classChunk != null : "Unable to find class chunk.";
return classChunk;
}
Expand Down Expand Up @@ -197,7 +197,7 @@ public Object[] getPointers(final int end) {
private Object decodePointer(final long ptr) {
switch ((int) (ptr & 7)) {
case SqueakImageConstants.OBJECT_TAG:
final SqueakImageChunk chunk = reader.getChunk(ptr);
final SqueakImageChunk chunk = reader.chunkMap.get(ptr);
if (chunk == null) {
logBogusPointer(ptr);
return ptr >>> SqueakImageConstants.NUM_TAG_BITS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
import java.io.BufferedInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.logging.Level;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleFile;
Expand All @@ -29,16 +29,17 @@
import de.hpi.swa.trufflesqueak.model.layout.ObjectLayouts.SPECIAL_OBJECT_TAG;
import de.hpi.swa.trufflesqueak.nodes.accessing.ArrayObjectNodes.ArrayObjectReadNode;
import de.hpi.swa.trufflesqueak.util.ArrayUtils;
import de.hpi.swa.trufflesqueak.util.LogUtils;
import de.hpi.swa.trufflesqueak.util.MiscUtils;
import de.hpi.swa.trufflesqueak.util.VarHandleUtils;

public final class SqueakImageReader {
private static final byte[] EMPTY_BYTES = new byte[0];

protected SqueakImageChunk hiddenRootsChunk;
public SqueakImageChunk hiddenRootsChunk;

private final HashMap<Long, SqueakImageChunk> chunktable = new HashMap<>(750000);
protected final SqueakImageContext image;
public final AddressToChunkMap chunkMap = new AddressToChunkMap();
public final SqueakImageContext image;
private final byte[] byteArrayBuffer = new byte[Long.BYTES];

private long oldBaseAddress;
Expand Down Expand Up @@ -176,7 +177,7 @@ private void readBody(final BufferedInputStream stream) throws IOException {
while (position < segmentEnd) {
while (position < segmentEnd - SqueakImageConstants.IMAGE_BRIDGE_SIZE) {
final SqueakImageChunk chunk = readObject(stream);
putChunk(chunk);
chunkMap.put(chunk.getPosition() + currentAddressSwizzle, chunk);
}
assert hiddenRootsChunk != null : "hiddenRootsChunk must be known from now on.";
final long bridge = nextLong(stream);
Expand All @@ -195,10 +196,6 @@ private void readBody(final BufferedInputStream stream) throws IOException {
assert dataSize == position;
}

private void putChunk(final SqueakImageChunk chunk) {
chunktable.put(chunk.getPosition() + currentAddressSwizzle, chunk);
}

private SqueakImageChunk readObject(final BufferedInputStream stream) throws IOException {
int pos = position;
assert pos % SqueakImageConstants.WORD_SIZE == 0 : "every object must be 64-bit aligned: " + pos % SqueakImageConstants.WORD_SIZE;
Expand Down Expand Up @@ -254,15 +251,15 @@ protected static boolean isObjectStack(final int classIndex, final int size) {
}

private SqueakImageChunk specialObjectChunk(final SqueakImageChunk specialObjectsChunk, final int idx) {
return getChunk(specialObjectsChunk.getWord(idx));
return chunkMap.get(specialObjectsChunk.getWord(idx));
}

private void setPrebuiltObject(final SqueakImageChunk specialObjectsChunk, final int idx, final Object object) {
specialObjectChunk(specialObjectsChunk, idx).setObject(object);
}

private void initPrebuiltConstant() {
final SqueakImageChunk specialChunk = getChunk(specialObjectsPointer);
final SqueakImageChunk specialChunk = chunkMap.get(specialObjectsPointer);
specialChunk.setObject(image.specialObjectsArray);

// first we find the Metaclass, we need it to correctly instantiate
Expand Down Expand Up @@ -328,19 +325,19 @@ private void fillInClassObjects() {
/** Find all metaclasses and instantiate their singleton instances as class objects. */
int highestKnownClassIndex = -1;
for (int p = 0; p < SqueakImageConstants.CLASS_TABLE_ROOT_SLOTS; p++) {
final SqueakImageChunk classTablePage = getChunk(hiddenRootsChunk.getWord(p));
final SqueakImageChunk classTablePage = chunkMap.get(hiddenRootsChunk.getWord(p));
if (classTablePage.isNil()) {
break; /* End of classTable reached (pages are consecutive). */
}
for (int i = 0; i < SqueakImageConstants.CLASS_TABLE_PAGE_SIZE; i++) {
final long potentialClassPtr = classTablePage.getWord(i);
assert potentialClassPtr != 0;
final SqueakImageChunk classChunk = getChunk(potentialClassPtr);
final SqueakImageChunk classChunk = chunkMap.get(potentialClassPtr);
if (classChunk.getSqueakClass() == image.metaClass) {
/* Derive classIndex from current position in class table. */
highestKnownClassIndex = p << SqueakImageConstants.CLASS_TABLE_MAJOR_INDEX_SHIFT | i;
assert classChunk.getWordSize() == METACLASS.INST_SIZE;
final SqueakImageChunk classInstance = getChunk(classChunk.getWord(METACLASS.THIS_CLASS));
final SqueakImageChunk classInstance = chunkMap.get(classChunk.getWord(METACLASS.THIS_CLASS));
final ClassObject metaClassObject = classChunk.asClassObject(image.metaClass);
metaClassObject.setInstancesAreClasses();
classInstance.asClassObject(metaClassObject);
Expand All @@ -351,7 +348,7 @@ private void fillInClassObjects() {
image.classTableIndex = highestKnownClassIndex;

/** Fill in metaClass. */
final SqueakImageChunk specialObjectsChunk = getChunk(specialObjectsPointer);
final SqueakImageChunk specialObjectsChunk = chunkMap.get(specialObjectsPointer);
final SqueakImageChunk sqArray = specialObjectsChunk.getClassChunk();
final SqueakImageChunk sqArrayClass = sqArray.getClassChunk();
final SqueakImageChunk sqMetaclass = sqArrayClass.getClassChunk();
Expand All @@ -367,17 +364,17 @@ private void fillInClassObjects() {
inst.add(classDescriptionClass);

for (int p = 0; p < SqueakImageConstants.CLASS_TABLE_ROOT_SLOTS; p++) {
final SqueakImageChunk classTablePage = getChunk(hiddenRootsChunk.getWord(p));
final SqueakImageChunk classTablePage = chunkMap.get(hiddenRootsChunk.getWord(p));
if (classTablePage.isNil()) {
break; /* End of classTable reached (pages are consecutive). */
}
for (int i = 0; i < SqueakImageConstants.CLASS_TABLE_PAGE_SIZE; i++) {
final long potentialClassPtr = classTablePage.getWord(i);
assert potentialClassPtr != 0;
final SqueakImageChunk classChunk = getChunk(potentialClassPtr);
final SqueakImageChunk classChunk = chunkMap.get(potentialClassPtr);
if (classChunk.getSqueakClass() == image.metaClass) {
assert classChunk.getWordSize() == METACLASS.INST_SIZE;
final SqueakImageChunk classInstance = getChunk(classChunk.getWord(METACLASS.THIS_CLASS));
final SqueakImageChunk classInstance = chunkMap.get(classChunk.getWord(METACLASS.THIS_CLASS));
final ClassObject metaClassObject = classChunk.asClassObject(image.metaClass);
final ClassObject classObject = classInstance.asClassObject(metaClassObject);
classObject.fillin(classInstance);
Expand All @@ -397,7 +394,10 @@ private void fillInClassObjects() {
}

private void fillInObjects() {
for (final SqueakImageChunk chunk : chunktable.values()) {
for (final SqueakImageChunk chunk : chunkMap.getChunks()) {
if (chunk == null) {
continue;
}
final Object chunkObject = chunk.asObject();
if (chunkObject instanceof final AbstractSqueakObjectWithClassAndHash obj) {
// FIXME:
Expand All @@ -413,7 +413,10 @@ private void fillInObjects() {
}

private void fillInContextObjects() {
for (final SqueakImageChunk chunk : chunktable.values()) {
for (final SqueakImageChunk chunk : chunkMap.getChunks()) {
if (chunk == null) {
continue;
}
final Object chunkObject = chunk.asObject();
if (chunkObject instanceof final ContextObject contextObject) {
assert !contextObject.hasTruffleFrame();
Expand All @@ -432,15 +435,11 @@ private void fillInClassesFromCompactClassList() {
private ClassObject lookupClassInCompactClassList(final int compactIndex) {
final int majorIndex = SqueakImageConstants.majorClassIndexOf(compactIndex);
final int minorIndex = SqueakImageConstants.minorClassIndexOf(compactIndex);
final ArrayObject classTablePage = (ArrayObject) getChunk(hiddenRootsChunk.getWord(majorIndex)).asObject();
final ArrayObject classTablePage = (ArrayObject) chunkMap.get(hiddenRootsChunk.getWord(majorIndex)).asObject();
final Object result = ArrayObjectReadNode.executeUncached(classTablePage, minorIndex);
return result instanceof final ClassObject c ? c : null;
}

protected SqueakImageChunk getChunk(final long ptr) {
return chunktable.get(ptr);
}

/* Calculate odd bits (see Behavior>>instSpec). */
public static int calculateObjectPadding(final int format) {
if (16 <= format && format <= 31) {
Expand All @@ -456,4 +455,64 @@ public static int calculateObjectPadding(final int format) {
return 0;
}
}

public static class AddressToChunkMap {
private static final int INITIAL_CAPACITY = 1_000_000;
private static final float THRESHOLD_PERCENTAGE = 0.75f;
private static final float RESIZE_FACTOR = 1.5f;
private static final int COLLISION_OFFSET = 31;

private int capacity = INITIAL_CAPACITY;
private int threshold = (int) (capacity * THRESHOLD_PERCENTAGE);
private long[] addresses = new long[capacity];
private SqueakImageChunk[] chunks = new SqueakImageChunk[capacity];
private int size;

public void put(final long address, final SqueakImageChunk chunk) {
if (size > threshold) {
resize();
}
int slot = (int) (address % capacity);
while (true) {
if (chunks[slot] == null) {
addresses[slot] = address;
chunks[slot] = chunk;
size++;
return;
}
slot = (slot + COLLISION_OFFSET) % capacity;
}
}

public SqueakImageChunk get(final long address) {
int slot = (int) (address % capacity);
while (true) {
if (addresses[slot] == address) {
return chunks[slot];
}
slot = (slot + COLLISION_OFFSET) % capacity;
}
}

private SqueakImageChunk[] getChunks() {
return chunks;
}

private void resize() {
capacity = (int) (capacity * RESIZE_FACTOR);
threshold = (int) (capacity * THRESHOLD_PERCENTAGE);
LogUtils.READER.log(Level.FINE, "Resizing chunk map to {0}", capacity);
final long[] oldAddresses = addresses;
final SqueakImageChunk[] oldChunks = chunks;
addresses = new long[capacity];
chunks = new SqueakImageChunk[capacity];
size = 0;
for (int i = 0; i < oldChunks.length; i++) {
final SqueakImageChunk chunk = oldChunks[i];
if (chunk != null) {
put(oldAddresses[i], chunk);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public final class LogUtils {
public static final TruffleLogger IO = TruffleLogger.getLogger(SqueakLanguageConfig.ID, "io");
public static final TruffleLogger ITERATE_FRAMES = TruffleLogger.getLogger(SqueakLanguageConfig.ID, "iterate-frames");
public static final TruffleLogger PRIMITIVES = TruffleLogger.getLogger(SqueakLanguageConfig.ID, "primitives");
public static final TruffleLogger READER = TruffleLogger.getLogger(SqueakLanguageConfig.ID, "reader");
public static final TruffleLogger SCHEDULING = TruffleLogger.getLogger(SqueakLanguageConfig.ID, "scheduling");
public static final TruffleLogger SOCKET = TruffleLogger.getLogger(SqueakLanguageConfig.ID, "socket");

Expand Down

1 comment on commit 0ae803a

@TruffleSqueak-Bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance Report (0ae803a)

Benchmarks ran on 22.0.2-graal.

Steady (after 100 iterations)

Benchmark Name Min Geomean Median Mean Max Total (ms) Total (min)
Bounce 518 585 527.29 522 526.97 105458 1.76
CD 488 501 491.5 489 491.48 98299 1.64
DeltaBlue 282 471 409.33 408 407.19 81865 1.36
Havlak 1107 1164 1140.46 1145 1140.38 228091 3.8
Json 369 379 372.21 370 372.19 74441 1.24
List 310 331 321.29 322 321.27 64257 1.07
Mandelbrot 127 136 127.77 128 127.76 25554 0.43
NBody 249 265 252.95 251.5 252.93 50590 0.84
Permute 154 169 155.66 155 155.64 31131 0.52
Queens 230 243 231.47 231 231.46 46293 0.77
Richards 1237 1251 1241.29 1242 1241.28 248257 4.14
Sieve 177 210 178.18 178 178.16 35636 0.59
Storage 142 153 144.43 143 144.41 28886 0.48
Towers 196 214 197.06 197 197.05 39411 0.66
5586 6072 5790.85 5781.5 5788.17 1158169 19.3

0ae803a-2-steady.svg

Warmup (first 100 iterations)

0ae803a-3-warmup.svg

Please sign in to comment.