Skip to content

Commit

Permalink
Simplify SqueakImageReader
Browse files Browse the repository at this point in the history
  • Loading branch information
fniephaus committed Feb 6, 2023
1 parent 804da32 commit 43d659c
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 118 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ public void testFloatDecoding() {

private static SqueakImageChunk newFloatChunk(final byte[] data) {
final SqueakImageChunk chunk = new SqueakImageChunk(
SqueakImageReader.createDummy(image),
new SqueakImageReader(image),
ObjectHeader.getHeader(0, 3833906, 10, 34),
0, // position
data // 2 words
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,7 @@ public final class SqueakImageChunk {
private ClassObject squeakClass;
private Object[] pointers;

public SqueakImageChunk(final SqueakImageReader reader,
final long header,
final int position,
final byte[] bytes) {
public SqueakImageChunk(final SqueakImageReader reader, final long header, final int position, final byte[] bytes) {
this.reader = reader;
this.header = header;
this.position = position;
Expand All @@ -51,7 +48,7 @@ public SqueakImageChunk(final SqueakImageReader reader,
}

public static SqueakImageChunk createDummyChunk(final SqueakImageContext image, final Object[] pointers) {
final SqueakImageChunk chunk = new SqueakImageChunk(SqueakImageReader.createDummy(image), 0, 0, new byte[0]);
final SqueakImageChunk chunk = new SqueakImageChunk(new SqueakImageReader(image), 0, 0, new byte[0]);
chunk.pointers = pointers;
return chunk;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ public final class SqueakImageReader {

protected SqueakImageChunk hiddenRootsChunk;

private final BufferedInputStream stream;
private final HashMap<Long, SqueakImageChunk> chunktable = new HashMap<>(750000);
protected final SqueakImageContext image;
private final byte[] byteArrayBuffer = new byte[Long.BYTES];
Expand All @@ -47,28 +46,12 @@ public final class SqueakImageReader {
private long firstSegmentSize;
private int position;
private long currentAddressSwizzle;
private long dataSize;

private SqueakImageChunk freePageList;

private SqueakImageReader(final SqueakImageContext image) {
final TruffleFile truffleFile = image.env.getPublicTruffleFile(image.getImagePath());
if (!truffleFile.isRegularFile()) {
throw SqueakException.create(MiscUtils.format("Image at '%s' does not exist.", image.getImagePath()));
}
BufferedInputStream inputStream = null;
try {
inputStream = new BufferedInputStream(truffleFile.newInputStream());
} catch (final IOException e) {
throw SqueakException.create(e);
}
stream = inputStream;
this.image = image;
}

/* For testing purposes only */
private SqueakImageReader(final SqueakImageContext image, final BufferedInputStream stream) {
public SqueakImageReader(final SqueakImageContext image) {
this.image = image;
this.stream = stream;
}

/*
Expand All @@ -81,178 +64,151 @@ public static void load(final SqueakImageContext image) {
System.gc(); // Clean up after image loading
}

public static SqueakImageReader createDummy(final SqueakImageContext image) {
return new SqueakImageReader(image, null);
}

private Object run() {
if (stream == null && image.isTesting()) {
return null;
}
SqueakImageContext.initializeBeforeLoadingImage();
final long start = MiscUtils.currentTimeMillis();
readHeader();
try {
readBody();
} finally {
closeStream();
final TruffleFile truffleFile = image.env.getPublicTruffleFile(image.getImagePath());
if (!truffleFile.isRegularFile()) {
throw SqueakException.create(MiscUtils.format("Image at '%s' does not exist.", image.getImagePath()));
}
try (BufferedInputStream inputStream = new BufferedInputStream(truffleFile.newInputStream());) {
readHeader(inputStream);
readBody(inputStream);
} catch (final IOException e) {
throw SqueakException.create("Failed to read Smalltalk image:", e.getMessage());
}
initObjects();
image.printToStdOut("Image loaded in", MiscUtils.currentTimeMillis() - start + "ms.");
image.setHiddenRoots((ArrayObject) hiddenRootsChunk.asObject());
return image.getSqueakImage();
}

private int readBytes(final byte[] bytes, final int length) {
try {
final int readBytes = stream.read(bytes, 0, length);
assert readBytes == length : "Failed to read bytes";
return readBytes;
} catch (final IOException e) {
throw SqueakException.create("Failed to read next bytes:", e.getMessage());
}
private static void readBytes(final BufferedInputStream stream, final byte[] bytes, final int length) throws IOException {
final int readBytes = stream.read(bytes, 0, length);
assert readBytes == length : "Failed to read bytes";
}

private long nextWord() {
return nextLong();
private long nextWord(final BufferedInputStream stream) throws IOException {
return nextLong(stream);
}

private short nextShort() {
position += readBytes(byteArrayBuffer, Short.BYTES);
private short nextShort(final BufferedInputStream stream) throws IOException {
readBytes(stream, byteArrayBuffer, Short.BYTES);
position += Short.BYTES;
return VarHandleUtils.getShort(byteArrayBuffer, 0);
}

private int nextInt() {
position += readBytes(byteArrayBuffer, Integer.BYTES);
private int nextInt(final BufferedInputStream stream) throws IOException {
readBytes(stream, byteArrayBuffer, Integer.BYTES);
position += Integer.BYTES;
return VarHandleUtils.getInt(byteArrayBuffer, 0);
}

private long nextLong() {
position += readBytes(byteArrayBuffer, Long.BYTES);
private long nextLong(final BufferedInputStream stream) throws IOException {
readBytes(stream, byteArrayBuffer, Long.BYTES);
position += Long.BYTES;
return VarHandleUtils.getLong(byteArrayBuffer, 0);
}

private byte[] nextObjectData(final int size, final int format) {
final int paddedObjectSize = size * SqueakImageConstants.WORD_SIZE;
final int padding = calculateObjectPadding(format);
final int dataSize = paddedObjectSize - padding;
private byte[] nextObjectData(final BufferedInputStream stream, final int size, final int format) throws IOException {
if (size == 0) {
skipBytes(SqueakImageConstants.WORD_SIZE); // skip trailing alignment word
skipBytes(stream, SqueakImageConstants.WORD_SIZE); // skip trailing alignment word
return EMPTY_BYTES;
}
final byte[] bytes = new byte[dataSize];
readBytes(bytes, dataSize);
try {
final long skipped = stream.skip(padding);
assert skipped == padding : "Failed to skip padding bytes";
} catch (final IOException e) {
throw SqueakException.create("Failed to skip next bytes:", e);
}
final int paddedObjectSize = size * SqueakImageConstants.WORD_SIZE;
final int padding = calculateObjectPadding(format);
final int objectDataSize = paddedObjectSize - padding;
final byte[] bytes = new byte[objectDataSize];
readBytes(stream, bytes, objectDataSize);
final long skipped = stream.skip(padding);
assert skipped == padding : "Failed to skip padding bytes";
position += paddedObjectSize;
return bytes;
}

private void skipBytes(final int count) {
private void skipBytes(final BufferedInputStream stream, final int count) throws IOException {
long pending = count;
try {
while (pending > 0) {
final long skipped = stream.skip(pending);
assert skipped > 0 : "Nothing skipped, reached EOF?";
pending -= skipped;
}
} catch (final IOException e) {
throw SqueakException.create("Failed to skip next bytes:", e);
while (pending > 0) {
final long skipped = stream.skip(pending);
assert skipped > 0 : "Nothing skipped, reached EOF?";
pending -= skipped;
}
position += count;
}

private void readVersion() {
image.imageFormat = nextInt();
private void readHeader(final BufferedInputStream stream) throws IOException {
image.imageFormat = nextInt(stream);
if (!ArrayUtils.contains(SqueakImageConstants.SUPPORTED_IMAGE_FORMATS, image.imageFormat)) {
throw SqueakException.create(MiscUtils.format("Image format %s not supported. Please supply a compatible 64bit Spur image (%s).", image.imageFormat,
Arrays.toString(SqueakImageConstants.SUPPORTED_IMAGE_FORMATS)));
}
// nextWord(); // magic2
}

private void readHeader() {
readVersion();

// Base header start
final int headerSize = nextInt();
nextWord(); // "length of heap in file"
oldBaseAddress = nextWord();
specialObjectsPointer = nextWord();
nextWord(); // 1 word last used hash
final long snapshotScreenSize = nextWord();
final long headerFlags = nextWord();
nextInt(); // extraVMMemory
final int headerSize = nextInt(stream);
dataSize = nextWord(stream);
oldBaseAddress = nextWord(stream);
specialObjectsPointer = nextWord(stream);
nextWord(stream); // 1 word last used hash
final long snapshotScreenSize = nextWord(stream);
final long headerFlags = nextWord(stream);
nextInt(stream); // extraVMMemory

// Spur header start
nextShort(); // numStackPages
nextShort(); // cogCodeSize
nextShort(stream); // numStackPages
nextShort(stream); // cogCodeSize
assert position == 64 : "Wrong position";
nextInt(); // edenBytes
final short maxExternalSemaphoreTableSize = nextShort();
nextShort(); // unused, realign to word boundary
nextInt(stream); // edenBytes
final short maxExternalSemaphoreTableSize = nextShort(stream);
nextShort(stream); // unused, realign to word boundary
assert position == 72 : "Wrong position";
firstSegmentSize = nextWord();
nextWord(); // freeOldSpace
firstSegmentSize = nextWord(stream);
nextWord(stream); // freeOldSpace

image.flags.initialize(oldBaseAddress, headerFlags, snapshotScreenSize, maxExternalSemaphoreTableSize);

skipBytes(headerSize - position); // skip to body
skipBytes(stream, headerSize - position); // skip to body
}

private void readBody() {
private void readBody(final BufferedInputStream stream) throws IOException {
position = 0;
long segmentEnd = firstSegmentSize;
currentAddressSwizzle = oldBaseAddress;
while (position < segmentEnd) {
while (position < segmentEnd - SqueakImageConstants.IMAGE_BRIDGE_SIZE) {
final SqueakImageChunk chunk = readObject();
final SqueakImageChunk chunk = readObject(stream);
putChunk(chunk);
}
assert hiddenRootsChunk != null : "hiddenRootsChunk must be known from now on.";
final long bridge = nextLong();
final long bridge = nextLong(stream);
long bridgeSpan = 0;
if ((bridge & SqueakImageConstants.SLOTS_MASK) != 0) {
bridgeSpan = bridge & ~SqueakImageConstants.SLOTS_MASK;
}
final long nextSegmentSize = nextLong();
assert bridgeSpan >= 0;
assert nextSegmentSize >= 0;
assert position == segmentEnd;
final long nextSegmentSize = nextLong(stream);
assert bridgeSpan >= 0 && nextSegmentSize >= 0 && position == segmentEnd;
if (nextSegmentSize == 0) {
break;
}
segmentEnd += nextSegmentSize;
currentAddressSwizzle += bridgeSpan * SqueakImageConstants.WORD_SIZE;
}
}

private void closeStream() {
try {
stream.close();
} catch (final IOException e) {
throw SqueakException.create("Failed to close stream:", e);
}
assert dataSize == position;
}

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

private SqueakImageChunk readObject() {
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;
long headerWord = nextLong();
long headerWord = nextLong(stream);
int numSlots = SqueakImageConstants.ObjectHeader.getNumSlots(headerWord);
if (numSlots == SqueakImageConstants.OVERFLOW_SLOTS) {
numSlots = (int) (headerWord & ~SqueakImageConstants.SLOTS_MASK);
assert numSlots >= SqueakImageConstants.OVERFLOW_SLOTS;
pos = position;
headerWord = nextLong();
headerWord = nextLong(stream);
assert SqueakImageConstants.ObjectHeader.getNumSlots(headerWord) == SqueakImageConstants.OVERFLOW_SLOTS : "Objects with long header must have 255 in slot count";
}
final int size = numSlots;
Expand All @@ -262,10 +218,10 @@ private SqueakImageChunk readObject() {
if (ignoreObjectData(headerWord, classIndex, size)) {
/* Skip some hidden objects for performance reasons. */
objectData = null;
skipBytes(size * SqueakImageConstants.WORD_SIZE);
skipBytes(stream, size * SqueakImageConstants.WORD_SIZE);
} else {
final int format = SqueakImageConstants.ObjectHeader.getFormat(headerWord);
objectData = nextObjectData(size, format);
objectData = nextObjectData(stream, size, format);
}
final SqueakImageChunk chunk = new SqueakImageChunk(this, headerWord, pos, objectData);
if (hiddenRootsChunk == null && isHiddenObject(classIndex)) {
Expand Down

1 comment on commit 43d659c

@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 (43d659c)

Benchmarks ran on graalvm-ce-java19-23.0.0-dev.

Steady (after 100 iterations)

Benchmark Name Min Geomean Median Mean Max Total (ms) Total (min)
Bounce 164 170 165.86 165 165.85 33172 0.55
CD 601 758 606.19 606 606.1 121238 2.02
DeltaBlue 319 508 427.3 436 425.82 85459 1.42
Havlak 1499 1559 1523.45 1520.5 1523.38 304689 5.08
Json 741 759 745.57 746 745.56 149113 2.49
List 334 352 335.55 335 335.54 67109 1.12
Mandelbrot 144 152 144.8 145 144.8 28960 0.48
NBody 263 276 266.59 266 266.58 53318 0.89
Permute 196 212 200.79 200 200.75 40157 0.67
Queens 204 214 204.92 205 204.92 40984 0.68
Richards 958 972 961.72 962 961.72 192344 3.21
Sieve 199 204 199.68 199 199.67 39935 0.67
Storage 252 264 256.75 257 256.73 51349 0.86
Towers 293 302 296.27 297 296.26 59254 0.99
6167 6702 6335.41 6339.5 6333.69 1267081 21.12

43d659c-2-steady.svg

Warmup (first 100 iterations)

43d659c-3-warmup.svg

Please sign in to comment.