-
Notifications
You must be signed in to change notification settings - Fork 46
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
Remove indify string concatenation on Inet::getInet4Address #93
Conversation
encodeUnsignedByte(s3, hostBytes, digitsForS1 + digitsForS2 + 2, digitsForS3); | ||
hostBytes[digitsForS1 + digitsForS2 + digitsForS3 + 2] = '.'; | ||
encodeUnsignedByte(s4, hostBytes, digitsForS1 + digitsForS2 + digitsForS3 + 3, digitsForS4); | ||
String hostName = new String(hostBytes, 0); |
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.
You can use new String(byte[],...,StandardCharsets.ISO_8859_1)
and it should be similar since it uses the same hot path with the charset is that one.
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.
explained at #93 (comment)
bytes[0] = (byte) s1; | ||
bytes[1] = (byte) s2; | ||
bytes[2] = (byte) s3; | ||
bytes[3] = (byte) s4; | ||
// pre-compute the digits required |
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.
You could avoid this (and some redundant branching) with something like:
byte[] hostBytes = new byte[15];
int cnt = encodeUnsignedByte(s1, hostBytes, 0);
hostBytes[cnt++] = '.';
cnt += encodeUnsignedByte(s2, hostBytes, cnt);
...
String hostName = new String(hostBytes, 0, cnt, StandardCharsets.ISO_8859_1);
and then:
private static int encodeUnsignedByte(int value, byte[] bytes, int offset) {
if (value >= 100) {
bytes[offset++] = '0' + value / 100;
value %= 100;
}
if (value >= 10) {
...
}
return offset;
}
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.
The redundant branching are there to avoid data dependency among encodeUnsignedByte
calls, which could be indipendent from each others thanks to the upfront estimation. But is a minor: I'm opened to change it if you prefer it.
for
String hostName = new String(hostBytes, 0, cnt, StandardCharsets.ISO_8859_1);
there's an opened JDK bug (I've provided the reproducer some time ago) which show that it perform very bad, still, see https://bugs.openjdk.org/browse/JDK-8295496
quoting it
I also included String(byte[], int, int, ISO_8859_1) which disappointingly lags slightly behind the deprecated String(byte[], int, int, int).
That's why I didn't used it.
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.
Additionally, I have sized correctly the byte array and not using any cnt
variable to simplify the life of the JDK stub generator and the bound checks generation, there, which proved to be the reason behind the bad performance of https://bugs.openjdk.org/browse/JDK-8295496
Also I would suggest instead making these changes to SmallRye Common Net - these two projects should be identical for this class, and it is my intention to deprecate this one and replace it with redirections to SmallRye Common. |
Perfect, sending the PR there as well! |
private static void encodeUnsignedByte(int value, byte[] bytes, int offset, int digits) { | ||
assert value >= 0 && value <= 255 && digits >= 1 && digits <= 3; | ||
if (digits == 3) { | ||
bytes[offset + 2] = (byte) ('0' + (value % 10)); |
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.
@dmlloyd FYI I didn't used the manual "strength reduction" to bit-shifts because:
- while the code is interpreted it seems there's no need, is already pretty slow for others reasons
- while the code is hot (C2, mostly), the JIT perform the same exact optimization
@radcortez let me know if this help your startup allocation analysis |
I can close it, to favour one to https://github.com/franz1981/wildfly-common instead |
@radcortez that's why: this has never been merged and relying on smallrye/smallrye-common#275, but it seems 3.8.2 still use this, suggestions @radcortez @dmlloyd ? |
The fix is in SR Commons: smallrye/smallrye-common#279 But the delegating code from WF Common to SR Common has not been released: We can replace the WF API with the SR API in the Converter so we don't have to wait for the release (and update in Quarkus) |
In Quarkus's startup we noticed some allocations due to https://openjdk.org/jeps/280, see
which point to the string concatenation in
Inet::getInet4Address
.The method could be optimized by:
StringBuilder
I've chosen the latter, to avoid the
StringBuilder
allocation, given that we have knowledge about the parameters ranges ie [0, 255].What could be done, better, is to save the intermediate
byte[] hostBytes
allocation to happen as a whole, by leveraging the defaultEliminateAllocationArraySizeLimit
in case OpenJDK escape analysis will do its, job ie creating a fixed size buffer of < 64 bytes (the default value - eg 15 bytes is the max size we could expect for255.255.255.255
).Obviously this is a dangerous bet, because rely on specific OpenJDK optimizations and when/if escape analysis kicks-in.