-
Notifications
You must be signed in to change notification settings - Fork 5.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8321620: Optimize JImage decompressors #16556
Conversation
👋 Welcome back Glavo! A progress list of the required criteria for merging this PR into |
I generated runtime images using @Warmup(iterations = 10, time = 2)
@Measurement(iterations = 5, time = 3)
@Fork(value = 1, jvmArgsAppend = {"-XX:+UseG1GC", "-Xms8g", "-Xmx8g", "--add-exports=java.base/jdk.internal.jimage=ALL-UNNAMED"})
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class Decompress {
private static final ImageReader READER = ImageReaderFactory.getImageReader();
private static final ImageLocation LOC = READER.findLocation("java.base", "java/lang/String.class");
@Benchmark
public ByteBuffer test() {
return READER.getResourceBuffer(LOC);
}
} This is the result:
The results show that memory allocation is reduced by more than 70% while decompression speed is significantly improved. |
I ran the tier1 and tier2 tests and there were no new errors. |
src/java.base/share/classes/jdk/internal/jimage/decompressor/StringSharingDecompressor.java
Outdated
Show resolved
Hide resolved
bytesOut[bytesOutOffset++] = (byte) ((count >> 8) & 0xff); | ||
bytesOut[bytesOutOffset++] = (byte) (count & 0xff); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably use ByteArray.setUnsignedShort
:
bytesOut[bytesOutOffset++] = (byte) ((count >> 8) & 0xff); | |
bytesOut[bytesOutOffset++] = (byte) (count & 0xff); | |
ByteArray.setUnsignedShort(bytesOut, bytesOutOffset, count); | |
bytesOutOffset += 2; |
Same remark elsewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These classes need to be compiled with --release 8
, so I can't use the ByteArray
.
I generated a runtime image containing javac using Baseline, No Compress: 10829ms String Share:
Zip:
You can see that in this test, this PR made the runtime image compressed using string share 30% faster. |
@@ -344,6 +345,23 @@ public String getString(int offset) { | |||
return ImageStringsReader.stringFromByteBuffer(strings, offset); | |||
} | |||
|
|||
int getStringMUTF8(int offset, byte[] bytesOut, int bytesOutOffset) { | |||
if (offset < 0 || offset >= strings.limit()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use Objects::checkIndex
here instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use
Objects::checkIndex
here instead?
No, because these classes need to be compiled with --release 8
.
Javadoc:
/**
* @implNote This class needs to maintain JDK 8 source compatibility.
*
* It is used internally in the JDK to implement jimage/jrtfs access,
* but also compiled and delivered as part of the jrtfs.jar to support access
* to the jimage file provided by the shipped JDK by tools running on JDK 8.
*/
out.writeShort(count); | ||
int offset, long originalSize) throws IOException { | ||
if (originalSize > Integer.MAX_VALUE) { | ||
throw new OutOfMemoryError("Required array size too large"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the correct exception type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the correct exception type?
I'm not sure, but I think it shouldn't matter, since the exception is never really thrown here for a valid jimage file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's common to throw OutOfMemoryError
when a requested array size would be too large, either due overflow or because some other limitation (see StringUTF16::newBytesFor
or String::replace
for some examples).
I'll need some time to review this thoroughly but skimming through it it seems like a very nice enhancement, making One gotcha in this area is that |
} | ||
} | ||
|
||
private void check(byte[] flow, List<byte[]> arrays) { | ||
List<Integer> d = CompressIndexes.decompressFlow(flow); | ||
List<Integer> d = Arrays.stream(CompressIndexes.decompressFlow(flow)).boxed().toList(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we rewrite this as
int[] d = CompressIndexes.decompressFlow(flow);
int[] dd = new int[arrays.length];
for (int j = 0; j < arrays.length; j++) {
byte[] b = arrays[j];
dd[j] = CompressIndexes.decompress(b, 0);
}
if (!Arrays.equals(d, dd) {
//...
}
The uncompressed runtime image used for comparison was also generated using jlink, without using CDS, so the comparison should be fair. |
Does anyone want to take a look at this PR? |
RFE: https://bugs.openjdk.org/browse/JDK-8321620 - update the bug ID and this should PR should reach a wider audience. I'll have some time next week to help review. |
@Glavo This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
/open |
@Glavo This pull request is already open |
Can anyone review this PR? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to merge with master to get jlink to work, perhaps some local issue since Classfile API has been moving around.
While the improvement to --compress 1
are impressive, this compression mode is effectively deprecated. I'm not sure your improvements to it here will be enough to reverse that decision (it's still a bit behind on all measures, no?). Perhaps it would be better to split out those changes and move forward with and focus this on the zip decompressor enhancements?
I noticed the deprecation warning before creating this PR, so I did some research. This warning was introduced here: #11617. In the discussion of that PR I learned that the option |
The plan [1] is to remove the old compression values |
I expected that few people use this option, because compared to zip, it is not superior in file size or decompression speed. However, I did some experiments in the JApp project and found that the file size using a mixture of string sharing + zstd compression was significantly smaller than using only zstd, so I thought it might be meaningful to study it further. Anyway, these are things for the future and I want this PR to stay simple, so I'll just drop these modifications. |
I created a new PR: #17405 I want to leave my changes to the string sharing plugin here so I can pick them up in the future. |
This PR significantly speeds up decompressing resources in Jimage while significantly reducing temporary memory allocations in the process.
This will improve startup speed for runtime images generated using
jlink --compress 1
andjlink --compress 2
.I generated a runtime image containing javac using
jlink --compress 1 --add-modules jdk.compiler
and tested the time it took to compile a simple HelloWorld program 20 times usingperf stat -r20 javac /dev/shm/HelloWorld.java
, this PR reduces the total time taken from 17830ms to 13598ms (31.12% faster).Progress
Issue
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/16556/head:pull/16556
$ git checkout pull/16556
Update a local copy of the PR:
$ git checkout pull/16556
$ git pull https://git.openjdk.org/jdk.git pull/16556/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 16556
View PR using the GUI difftool:
$ git pr show -t 16556
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/16556.diff
Webrev
Link to Webrev Comment