Skip to content

Commit

Permalink
Read pointers directly from chunks.
Browse files Browse the repository at this point in the history
This reduces the amount of Object[] allocated, and thus memory pressure, during startup.
  • Loading branch information
fniephaus committed Oct 14, 2024
1 parent 0ae803a commit 92e2013
Show file tree
Hide file tree
Showing 13 changed files with 81 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import org.junit.AfterClass;
import org.junit.BeforeClass;

import de.hpi.swa.trufflesqueak.image.SqueakImageChunk;
import de.hpi.swa.trufflesqueak.image.SqueakImageContext;
import de.hpi.swa.trufflesqueak.model.ArrayObject;
import de.hpi.swa.trufflesqueak.model.ClassObject;
Expand All @@ -34,30 +33,30 @@ public static void setUpSqueakImageContext() {
dummySpecialSelectors.setSqueakClass(image.arrayClass);

image.setByteSymbolClass(new ClassObject(image));
setupMeta(image.metaClass, new Object[]{
null, null, 0L, null, null, null, asByteSymbol("Metaclass"), null, null, null, null});
setupMeta(image.getByteSymbolClass(), new Object[]{
null, null, 0L, null, null, null, asByteSymbol("ByteSymbol"), null, null, null, null});
setupMeta(image.compiledMethodClass, new Object[]{null, null, 1572864L, null, null, null, asByteSymbol("CompiledMethod"), null, null, null, null});
setupMeta(image.nilClass, new Object[]{
null, null, 0L, null, null, null, asByteSymbol("UndefinedObject"), null, null, null, null});
setupMeta(image.arrayClass, new Object[]{
null, null, 0L, null, null, null, asByteSymbol("Array"), null, null, null, null});
setupMeta(image.metaClass, null, 0L, new Object[]{null, asByteSymbol("Metaclass"), null, null, null, null});
setupMeta(image.getByteSymbolClass(), null, 0L, new Object[]{null, asByteSymbol("ByteSymbol"), null, null, null, null});
setupMeta(image.compiledMethodClass, null, 1572864L, new Object[]{null, asByteSymbol("CompiledMethod"), null, null, null, null});
setupMeta(image.nilClass, null, 0L, new Object[]{null, asByteSymbol("UndefinedObject"), null, null, null, null});
setupMeta(image.arrayClass, null, 0L, new Object[]{null, asByteSymbol("Array"), null, null, null, null});

final ClassObject bindingClass = setupMeta(new ClassObject(image), new Object[]{
null, null, 1L, null, null, null, asByteSymbol("Binding"), null, null, null, null});
final ClassObject classBindingClass = setupMeta(new ClassObject(image), new Object[]{
bindingClass, null, 2L, null, null, null, asByteSymbol("ClassBinding"), null, null, null, null});
nilClassBinding = new PointersObject(image, classBindingClass, null);
nilClassBinding.fillin(SqueakImageChunk.createDummyChunk(image, new Object[]{asByteSymbol("UndefinedObject"), image.nilClass}));
final ClassObject bindingClass = setupMeta(new ClassObject(image), null, 1L, new Object[]{null, asByteSymbol("Binding"), null, null, null, null});
final ClassObject classBindingClass = setupMeta(new ClassObject(image), bindingClass, 2L, new Object[]{null, asByteSymbol("ClassBinding"), null, null, null, null});
nilClassBinding = new PointersObject(image, classBindingClass, classBindingClass.getLayout());
nilClassBinding.instVarAtPut0Slow(0, asByteSymbol("UndefinedObject"));
nilClassBinding.instVarAtPut0Slow(1, image.nilClass);

image.setHiddenRoots(ArrayObject.createEmptyStrategy(image, image.arrayClass, 0));
context.enter();
}

private static ClassObject setupMeta(final ClassObject aClass, final Object[] pointers) {
final SqueakImageChunk fakeChunk = SqueakImageChunk.createDummyChunk(image, pointers);
aClass.fillin(fakeChunk);
private static ClassObject setupMeta(final ClassObject aClass, final ClassObject superclass, final long format, final Object[] otherPointers) {
aClass.setSuperclass(superclass);
aClass.setMethodDict(null);
aClass.setFormat(format);
aClass.setInstanceVariables(null);
aClass.setOrganization(null);
aClass.setOtherPointers(otherPointers);

final ClassObject aClassClass = new ClassObject(image, image.metaClass, METACLASS.INST_SIZE);
aClassClass.setInstancesAreClasses();
aClass.setSqueakClass(aClassClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

import org.junit.Test;

import de.hpi.swa.trufflesqueak.image.SqueakImageChunk;
import de.hpi.swa.trufflesqueak.model.AbstractPointersObject;
import de.hpi.swa.trufflesqueak.model.ClassObject;
import de.hpi.swa.trufflesqueak.model.NilObject;
Expand All @@ -24,6 +23,7 @@
import de.hpi.swa.trufflesqueak.nodes.accessing.AbstractPointersObjectNodes.AbstractPointersObjectReadNode;
import de.hpi.swa.trufflesqueak.nodes.accessing.AbstractPointersObjectNodes.AbstractPointersObjectWriteNode;
import de.hpi.swa.trufflesqueak.nodes.accessing.SqueakObjectNewNode;
import de.hpi.swa.trufflesqueak.util.ArrayUtils;

@SuppressWarnings("static-method")
public final class ObjectLayoutTest extends AbstractSqueakTestCaseWithDummyImage {
Expand Down Expand Up @@ -181,13 +181,8 @@ private static void assertUnsetObjectFields(final AbstractPointersObject obj) {

private static ClassObject createFreshTestClass() {
final ClassObject dummyClass = new ClassObject(image);
final SqueakImageChunk dummyChunk = SqueakImageChunk.createDummyChunk(image, new Object[]{
image.nilClass.getSuperclass(), null,
// Format:
65542L /* `Morph format` */ | 24 /* + 24 slot = 30 slots in total. */,
null, null
});
dummyClass.fillin(dummyChunk);
dummyClass.setFormat(65542L /* `Morph format` */ | 24 /* + 24 slot = 30 slots in total. */);
dummyClass.setOtherPointers(ArrayUtils.EMPTY_ARRAY);
return dummyClass;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ public final class SqueakImageChunk {

private Object object;
private ClassObject squeakClass;
private Object[] pointers;

public SqueakImageChunk(final SqueakImageReader reader, final long header, final int position, final byte[] bytes) {
this.reader = reader;
Expand All @@ -47,12 +46,6 @@ public SqueakImageChunk(final SqueakImageReader reader, final long header, final
}
}

public static SqueakImageChunk createDummyChunk(final SqueakImageContext image, final Object[] pointers) {
final SqueakImageChunk chunk = new SqueakImageChunk(new SqueakImageReader(image), 0, 0, new byte[0]);
chunk.pointers = pointers;
return chunk;
}

public ClassObject asClassObject(final ClassObject metaClassObject) {
if (object == null) {
assert getFormat() == 1;
Expand Down Expand Up @@ -173,25 +166,21 @@ public void setSqueakClass(final ClassObject baseSqueakObject) {
squeakClass = baseSqueakObject;
}

public Object[] getPointers() {
if (pointers == null) {
final int length = getWordSize();
pointers = new Object[length];
for (int i = 0; i < length; i++) {
pointers[i] = decodePointer(getWord(i));
}
}
return pointers;
public Object getPointer(final int index) {
return decodePointer(getWord(index));
}

public Object[] getPointers(final int end) {
if (pointers == null) {
pointers = new Object[end];
for (int i = 0; i < end; i++) {
pointers[i] = decodePointer(getWord(i));
}
public Object[] getPointers(final int start) {
return getPointers(start, getWordSize());
}

public Object[] getPointers(final int start, final int end) {
final int numObjects = end - start;
final Object[] result = new Object[numObjects];
for (int i = 0; i < numObjects; i++) {
result[i] = getPointer(start + i);
}
return pointers;
return result;
}

private Object decodePointer(final long ptr) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,16 +119,15 @@ public final void fillin(final SqueakImageChunk chunk) {
primitiveExtension = layout.getFreshPrimitiveExtension();
objectExtension = layout.getFreshObjectExtension();
final AbstractPointersObjectWriteNode writeNode = AbstractPointersObjectWriteNode.getUncached();
final Object[] pointers = chunk.getPointers();
final int instSize = instsize();
for (int i = 0; i < instSize; i++) {
writeNode.execute(null, this, i, pointers[i]);
writeNode.execute(null, this, i, chunk.getPointer(i));
}
fillInVariablePart(pointers, instSize);
assert size() == pointers.length;
fillInVariablePart(chunk, instSize);
assert size() == chunk.getWordSize();
}

protected abstract void fillInVariablePart(Object[] pointers, int instSize);
protected abstract void fillInVariablePart(SqueakImageChunk chunk, int instSize);

public final ObjectLayout getLayout() {
if (layout == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
*/
package de.hpi.swa.trufflesqueak.model;

import java.util.Arrays;

import com.oracle.truffle.api.nodes.Node;

import de.hpi.swa.trufflesqueak.image.SqueakImageChunk;
import de.hpi.swa.trufflesqueak.image.SqueakImageContext;
import de.hpi.swa.trufflesqueak.model.layout.ObjectLayout;
import de.hpi.swa.trufflesqueak.nodes.accessing.SqueakObjectIdentityNode;
Expand All @@ -34,8 +33,8 @@ protected AbstractVariablePointersObject(final AbstractVariablePointersObject or
}

@Override
protected void fillInVariablePart(final Object[] pointers, final int instSize) {
variablePart = Arrays.copyOfRange(pointers, instSize, pointers.length);
protected void fillInVariablePart(final SqueakImageChunk chunk, final int instSize) {
variablePart = chunk.getPointers(instSize);
}

public final void become(final AbstractVariablePointersObject other) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,13 @@ public static boolean isLongNilTag(final long value) {

@Override
public void fillin(final SqueakImageChunk chunk) {
final Object[] pointers = chunk.getPointers();
final int valuesLength = pointers.length;
final int valuesLength = chunk.getWordSize();
storage = valuesLength;
if (valuesLength > 0) {
// Use a fresh write node because uncached node is too generic.
final ArrayObjectWriteNode writeNode = ArrayObjectWriteNode.create();
for (int i = 0; i < valuesLength; i++) {
writeNode.execute(writeNode, this, i, pointers[i]);
writeNode.execute(writeNode, this, i, chunk.getPointer(i));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
*/
package de.hpi.swa.trufflesqueak.model;

import java.util.Arrays;

import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
Expand Down Expand Up @@ -72,18 +70,18 @@ public static BlockClosureObject create(final SqueakImageContext image, final Cl
@Override
public void fillin(final SqueakImageChunk chunk) {
CompilerDirectives.transferToInterpreterAndInvalidate();
final Object[] pointers = chunk.getPointers();
assert pointers.length >= BLOCK_CLOSURE.FIRST_COPIED_VALUE;
outerContext = (ContextObject) pointers[BLOCK_CLOSURE.OUTER_CONTEXT];
if (pointers[BLOCK_CLOSURE.START_PC_OR_METHOD] instanceof final CompiledCodeObject code) {
assert chunk.getWordSize() >= BLOCK_CLOSURE.FIRST_COPIED_VALUE;
outerContext = (ContextObject) chunk.getPointer(BLOCK_CLOSURE.OUTER_CONTEXT);
final Object startPCOrMethod = chunk.getPointer(BLOCK_CLOSURE.START_PC_OR_METHOD);
if (startPCOrMethod instanceof final CompiledCodeObject code) {
block = code;
receiver = pointers[BLOCK_CLOSURE.FULL_RECEIVER];
copiedValues = Arrays.copyOfRange(pointers, BLOCK_CLOSURE.FULL_FIRST_COPIED_VALUE, pointers.length);
receiver = chunk.getPointer(BLOCK_CLOSURE.FULL_RECEIVER);
copiedValues = chunk.getPointers(BLOCK_CLOSURE.FULL_FIRST_COPIED_VALUE);
} else {
startPC = (long) pointers[BLOCK_CLOSURE.START_PC_OR_METHOD];
copiedValues = Arrays.copyOfRange(pointers, BLOCK_CLOSURE.FIRST_COPIED_VALUE, pointers.length);
startPC = (long) startPCOrMethod;
copiedValues = chunk.getPointers(BLOCK_CLOSURE.FIRST_COPIED_VALUE);
}
numArgs = (long) pointers[BLOCK_CLOSURE.ARGUMENT_COUNT];
numArgs = (long) chunk.getPointer(BLOCK_CLOSURE.ARGUMENT_COUNT);
}

public AbstractSqueakObject getOuterContext() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
*/
package de.hpi.swa.trufflesqueak.model;

import java.util.Arrays;

import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
Expand Down Expand Up @@ -257,13 +255,12 @@ public void fillin(final SqueakImageChunk chunk) {
if (needsSqueakHash() && chunk.getHash() != 0) {
setSqueakHash(chunk.getHash());
}
final Object[] chunkPointers = chunk.getPointers();
superclass = chunkPointers[CLASS_DESCRIPTION.SUPERCLASS] == NilObject.SINGLETON ? null : (ClassObject) chunkPointers[CLASS_DESCRIPTION.SUPERCLASS];
methodDict = (VariablePointersObject) chunkPointers[CLASS_DESCRIPTION.METHOD_DICT];
format = (long) chunkPointers[CLASS_DESCRIPTION.FORMAT];
instanceVariables = chunkPointers[CLASS_DESCRIPTION.INSTANCE_VARIABLES] == NilObject.SINGLETON ? null : (ArrayObject) chunkPointers[CLASS_DESCRIPTION.INSTANCE_VARIABLES];
organization = chunkPointers[CLASS_DESCRIPTION.ORGANIZATION] == NilObject.SINGLETON ? null : (PointersObject) chunkPointers[CLASS_DESCRIPTION.ORGANIZATION];
pointers = Arrays.copyOfRange(chunkPointers, CLASS_DESCRIPTION.SIZE, chunkPointers.length);
superclass = (ClassObject) NilObject.nilToNull(chunk.getPointer(CLASS_DESCRIPTION.SUPERCLASS));
methodDict = (VariablePointersObject) chunk.getPointer(CLASS_DESCRIPTION.METHOD_DICT);
format = (long) chunk.getPointer(CLASS_DESCRIPTION.FORMAT);
instanceVariables = (ArrayObject) NilObject.nilToNull(chunk.getPointer(CLASS_DESCRIPTION.INSTANCE_VARIABLES));
organization = (PointersObject) NilObject.nilToNull(chunk.getPointer(CLASS_DESCRIPTION.ORGANIZATION));
pointers = chunk.getPointers(CLASS_DESCRIPTION.SIZE);
}
}

Expand Down Expand Up @@ -373,7 +370,7 @@ public Object[] getOtherPointers() {
return pointers;
}

private void setOtherPointers(final Object[] pointers) {
public void setOtherPointers(final Object[] pointers) {
this.pointers = pointers;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ public void fillin(final SqueakImageChunk chunk) {
final long header = chunk.getWord(0) >> SqueakImageConstants.NUM_TAG_BITS;
numLiterals = CompiledCodeHeaderDecoder.getNumLiterals(header);
assert literals == null;
literals = chunk.getPointers(1 + numLiterals);
literals = chunk.getPointers(0, 1 + numLiterals);
decodeHeader();
assert bytes == null;
bytes = Arrays.copyOfRange(chunk.getBytes(), literals.length * SqueakImageConstants.WORD_SIZE, chunk.getBytes().length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,12 @@ public void fillin(final SqueakImageChunk chunk) {
}

public void fillinContext(final SqueakImageChunk chunk) {
final Object[] pointers = chunk.getPointers();
size = pointers.length;
size = chunk.getWordSize();
assert size > CONTEXT.TEMP_FRAME_START;
final CompiledCodeObject method = (CompiledCodeObject) pointers[CONTEXT.METHOD];
final AbstractSqueakObject sender = (AbstractSqueakObject) pointers[CONTEXT.SENDER_OR_NIL];
final CompiledCodeObject method = (CompiledCodeObject) chunk.getPointer(CONTEXT.METHOD);
final AbstractSqueakObject sender = (AbstractSqueakObject) chunk.getPointer(CONTEXT.SENDER_OR_NIL);
assert sender != null : "sender should not be null";
final Object closureOrNil = pointers[CONTEXT.CLOSURE_OR_NIL];
final Object closureOrNil = chunk.getPointer(CONTEXT.CLOSURE_OR_NIL);
final BlockClosureObject closure;
final int numArgs;
final CompiledCodeObject methodOrBlock;
Expand All @@ -136,22 +135,22 @@ public void fillinContext(final SqueakImageChunk chunk) {
}
}
final int endArguments = CONTEXT.TEMP_FRAME_START + numArgs;
final Object[] arguments = Arrays.copyOfRange(pointers, CONTEXT.RECEIVER, endArguments);
final Object[] arguments = chunk.getPointers(CONTEXT.RECEIVER, endArguments);
final Object[] frameArguments = FrameAccess.newWith(sender, closure, arguments);
CompilerDirectives.transferToInterpreterAndInvalidate();
truffleFrame = Truffle.getRuntime().createMaterializedFrame(frameArguments, methodOrBlock.getFrameDescriptor());
FrameAccess.initializeMarker(truffleFrame);
FrameAccess.setContext(truffleFrame, this);
final Object pc = pointers[CONTEXT.INSTRUCTION_POINTER];
final Object pc = chunk.getPointer(CONTEXT.INSTRUCTION_POINTER);
if (pc == NilObject.SINGLETON) {
removeInstructionPointer();
} else {
setInstructionPointer(MiscUtils.toIntExact((long) pc));
}
final int stackPointer = MiscUtils.toIntExact((long) pointers[CONTEXT.STACKPOINTER]);
final int stackPointer = MiscUtils.toIntExact((long) chunk.getPointer(CONTEXT.STACKPOINTER));
setStackPointer(stackPointer);
for (int i = 0; i < stackPointer; i++) {
atTempPut(i, pointers[CONTEXT.TEMP_FRAME_START + i]);
atTempPut(i, chunk.getPointer(CONTEXT.TEMP_FRAME_START + i));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public static Object nullToNil(final Object object) {
return object == null ? SINGLETON : object;
}

public static Object nilToNull(final Object object) {
return object == SINGLETON ? null : object;
}

@Override
public long getOrCreateSqueakHash() {
return SQUEAK_HASH;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.nodes.Node;

import de.hpi.swa.trufflesqueak.image.SqueakImageChunk;
import de.hpi.swa.trufflesqueak.image.SqueakImageContext;
import de.hpi.swa.trufflesqueak.image.SqueakImageWriter;
import de.hpi.swa.trufflesqueak.model.layout.ObjectLayout;
Expand Down Expand Up @@ -58,9 +59,9 @@ public void setHiddenObject(final Object value) {
}

@Override
protected void fillInVariablePart(final Object[] pointers, final int instSize) {
protected void fillInVariablePart(final SqueakImageChunk chunk, final int instSize) {
// No variable part to fill in
assert pointers.length == instSize : "Unexpected number of pointers found for " + this;
assert chunk.getWordSize() == instSize : "Unexpected number of pointers found for " + this;
}

public void become(final PointersObject other) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.InlinedConditionProfile;

import de.hpi.swa.trufflesqueak.image.SqueakImageChunk;
import de.hpi.swa.trufflesqueak.image.SqueakImageContext;
import de.hpi.swa.trufflesqueak.image.SqueakImageWriter;
import de.hpi.swa.trufflesqueak.model.layout.ObjectLayout;
Expand All @@ -39,13 +40,12 @@ private WeakVariablePointersObject(final WeakVariablePointersObject original) {
}

@Override
protected void fillInVariablePart(final Object[] pointers, final int instSize) {
super.fillInVariablePart(pointers, instSize);
for (int i = 0; i < variablePart.length; i++) {
final Object value = variablePart[i];
if (value instanceof final AbstractSqueakObject o) {
variablePart[i] = new WeakRef(o, weakPointersQueue);
}
protected void fillInVariablePart(final SqueakImageChunk chunk, final int instSize) {
final int numVariableSlots = chunk.getWordSize() - instSize;
variablePart = new Object[numVariableSlots];
for (int i = 0; i < numVariableSlots; i++) {
final Object value = chunk.getPointer(instSize + i);
variablePart[i] = value instanceof final AbstractSqueakObject o ? new WeakRef(o, weakPointersQueue) : value;
}
}

Expand Down

1 comment on commit 92e2013

@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 (92e2013)

Benchmarks ran on 22.0.2-graal.

Steady (after 100 iterations)

Benchmark Name Min Geomean Median Mean Max Total (ms) Total (min)
Bounce 586 596 588.77 587 588.76 117754 1.96
CD 487 498 490.46 488 490.44 98091 1.63
DeltaBlue 278 478 417.37 416.5 415.61 83473 1.39
Havlak 1121 1182 1152.87 1157 1152.8 230574 3.84
Json 375 385 377.96 376 377.95 75591 1.26
List 309 323 309.88 310 309.87 61976 1.03
Mandelbrot 127 139 127.76 127 127.74 25551 0.43
NBody 249 267 252.76 251 252.73 50551 0.84
Permute 154 166 155.94 155 155.92 31187 0.52
Queens 230 269 245.03 237 244.71 49005 0.82
Richards 1240 1294 1244.93 1245 1244.91 248986 4.15
Sieve 177 187 178.19 178 178.18 35637 0.59
Storage 147 179 148.71 147 148.68 29742 0.5
Towers 196 212 199.46 199 199.44 39892 0.66
5676 6175 5890.05 5873.5 5887.76 1178010 19.63

92e2013-2-steady.svg

Warmup (first 100 iterations)

92e2013-3-warmup.svg

Please sign in to comment.