Skip to content

Commit 42a4b60

Browse files
committed
[GR-19220] Faster solution for lazy integer length (#2365)
PullRequest: truffleruby/2683
2 parents c04323c + dfebbdb commit 42a4b60

File tree

2 files changed

+29
-11
lines changed

2 files changed

+29
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Performance:
4343
* Improved the performance of `IO#read_nonblock` when no data is available to be read.
4444
* `TruffleSafepoint` is now used instead of custom logic, which no longer invalidates JITed code for guest safepoints (e.g., `Thread#{backtrace,raise,kill}`, `ObjectSpace`, etc)
4545
* Significantly improved performance of `Time#strftime` for common formats (#2361, @wildmaples, @chrisseaton).
46+
* Faster solution for lazy integer length (#2365, @lemire, @chrisseaton).
4647

4748
Changes:
4849

src/main/java/org/truffleruby/core/rope/LazyIntRope.java

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
package org.truffleruby.core.rope;
1111

1212
import com.oracle.truffle.api.CompilerDirectives;
13+
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
1314
import com.oracle.truffle.api.profiles.ConditionProfile;
1415
import org.jcodings.Encoding;
1516
import org.jcodings.specific.USASCIIEncoding;
@@ -27,29 +28,45 @@ public LazyIntRope(int value) {
2728
public LazyIntRope(int value, Encoding encoding, int length) {
2829
super(encoding, CodeRange.CR_7BIT, length, length, null);
2930
this.value = value;
30-
assert Integer.toString(value).length() == length;
31+
assert Integer.toString(value).length() == length : value + " " + length;
3132
}
3233

34+
// @formatter:off
35+
@CompilationFinal(dimensions = 1) private static final long[] LENGTH_TABLE = {
36+
0x100000000L, 0x1FFFFFFF6L, 0x1FFFFFFF6L,
37+
0x1FFFFFFF6L, 0x2FFFFFF9CL, 0x2FFFFFF9CL,
38+
0x2FFFFFF9CL, 0x3FFFFFC18L, 0x3FFFFFC18L,
39+
0x3FFFFFC18L, 0x4FFFFD8F0L, 0x4FFFFD8F0L,
40+
0x4FFFFD8F0L, 0x4FFFFD8F0L, 0x5FFFE7960L,
41+
0x5FFFE7960L, 0x5FFFE7960L, 0x6FFF0BDC0L,
42+
0x6FFF0BDC0L, 0x6FFF0BDC0L, 0x7FF676980L,
43+
0x7FF676980L, 0x7FF676980L, 0x7FF676980L,
44+
0x8FA0A1F00L, 0x8FA0A1F00L, 0x8FA0A1F00L,
45+
0x9C4653600L, 0x9C4653600L, 0x9C4653600L,
46+
0xA00000000L, 0xA00000000L
47+
};
48+
// @formatter:on
49+
50+
// From https://lemire.me/blog/2021/06/03/computing-the-number-of-digits-of-an-integer-even-faster/
51+
// and https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/blob/4e6e171a7d/2021/06/03/digitcount.c (license: public domain)
3352
private static int length(int value) {
3453
final int sign;
35-
36-
if (value < 0) {
37-
/* We can't represent -Integer.MIN_VALUE, and we're about to multiple by 10 to add the space needed for the
38-
* negative character, so handle both of those out-of-range cases. */
39-
40-
if (value <= -1000000000) {
54+
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.UNLIKELY_PROBABILITY, value < 0)) {
55+
// We can't represent -Integer.MIN_VALUE (it results in Integer.MIN_VALUE), so we need to handle it explicitly
56+
if (CompilerDirectives
57+
.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, value == Integer.MIN_VALUE)) {
4158
return 11;
4259
}
4360

44-
value = -value;
4561
sign = 1;
62+
value = -value;
4663
} else {
4764
sign = 0;
4865
}
4966

50-
return sign + (value < 1E5
51-
? value < 1E2 ? value < 1E1 ? 1 : 2 : value < 1E3 ? 3 : value < 1E4 ? 4 : 5
52-
: value < 1E7 ? value < 1E6 ? 6 : 7 : value < 1E8 ? 8 : value < 1E9 ? 9 : 10);
67+
final int bits = 31 - Integer.numberOfLeadingZeros(value | 1);
68+
int digits = (int) ((value + LENGTH_TABLE[bits]) >>> 32);
69+
return sign + digits;
5370
}
5471

5572
@Override

0 commit comments

Comments
 (0)