Skip to content

Commit 40a2425

Browse files
committed
[GR-19220] Significantly improved performance of Time#strftime for common formats (#2361)
PullRequest: truffleruby/2682
2 parents a148162 + c184f87 commit 40a2425

File tree

11 files changed

+259
-43
lines changed

11 files changed

+259
-43
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Performance:
4242
* Improved the performance of the exceptional case of `String#chr` (#2318, @chrisseaton).
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)
45+
* Significantly improved performance of `Time#strftime` for common formats (#2361, @wildmaples, @chrisseaton).
4546

4647
Changes:
4748

spec/ruby/library/datetime/strftime_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
require_relative '../../spec_helper'
12
require 'date'
23
require_relative '../../shared/time/strftime_for_date'
34
require_relative '../../shared/time/strftime_for_time'

spec/ruby/shared/time/strftime_for_time.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,12 @@
170170
time.strftime("%-k%p").should == "8AM"
171171
time.strftime("%-l%p").should == "8AM"
172172
end
173+
174+
it "should be able to show default Logger format" do
175+
default_logger_format = "%Y-%m-%dT%H:%M:%S.%6N "
176+
@new_time[2001, 2, 3, 4, 5, 6].strftime(default_logger_format).should == "2001-02-03T04:05:06.000000 "
177+
@new_time[2001, 12, 3, 4, 5, 6 + 1/10r].strftime(default_logger_format).should == "2001-12-03T04:05:06.100000 "
178+
@new_time[2001, 2, 13, 4, 5, 6 + 1/100r].strftime(default_logger_format).should == "2001-02-13T04:05:06.010000 "
179+
@new_time[2001, 2, 3, 14, 5, 6 + 1/1000r].strftime(default_logger_format).should == "2001-02-03T14:05:06.001000 "
180+
end
173181
end

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public LazyIntRope(int value) {
2424
this(value, USASCIIEncoding.INSTANCE, length(value));
2525
}
2626

27-
protected LazyIntRope(int value, Encoding encoding, int length) {
27+
public LazyIntRope(int value, Encoding encoding, int length) {
2828
super(encoding, CodeRange.CR_7BIT, length, length, null);
2929
this.value = value;
3030
assert Integer.toString(value).length() == length;

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,4 @@ public byte getByteSlow(int index) {
2222
return getRawBytes()[index];
2323
}
2424

25-
public LeafRope computeHashCode() {
26-
hashCode();
27-
return this;
28-
}
29-
3025
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@ public void append(Rope other) {
6262
append(other.getBytes());
6363
}
6464

65-
public Rope toRope() {
65+
public ManagedRope toRope() {
6666
return toRope(CR_UNKNOWN);
6767
}
6868

69-
public Rope toRope(CodeRange codeRange) {
69+
public ManagedRope toRope(CodeRange codeRange) {
7070
// TODO CS 17-Jan-16 can we take the bytes from the RopeBuilder and set its bytes to null so it can't use them again
7171
return RopeOperations.create(getBytes(), encoding, codeRange);
7272
}

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

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,35 +18,36 @@
1818

1919
public class RopeConstants {
2020

21+
public static final Map<String, LeafRope> ROPE_CONSTANTS = new HashMap<>();
22+
2123
public static final byte[] EMPTY_BYTES = new byte[0];
2224

23-
public static final LeafRope EMPTY_ASCII_8BIT_ROPE = new AsciiOnlyLeafRope(EMPTY_BYTES, ASCIIEncoding.INSTANCE)
24-
.computeHashCode();
25-
public static final LeafRope EMPTY_US_ASCII_ROPE = new AsciiOnlyLeafRope(EMPTY_BYTES, USASCIIEncoding.INSTANCE)
26-
.computeHashCode();
27-
public static final LeafRope EMPTY_UTF8_ROPE = new AsciiOnlyLeafRope(EMPTY_BYTES, UTF8Encoding.INSTANCE)
28-
.computeHashCode();
25+
public static final LeafRope EMPTY_ASCII_8BIT_ROPE = withHashCode(
26+
new AsciiOnlyLeafRope(EMPTY_BYTES, ASCIIEncoding.INSTANCE));
27+
public static final LeafRope EMPTY_US_ASCII_ROPE = withHashCode(
28+
new AsciiOnlyLeafRope(EMPTY_BYTES, USASCIIEncoding.INSTANCE));
29+
public static final LeafRope EMPTY_UTF8_ROPE = withHashCode(
30+
new AsciiOnlyLeafRope(EMPTY_BYTES, UTF8Encoding.INSTANCE));
2931

3032
public static final LeafRope[] UTF8_SINGLE_BYTE_ROPES = new LeafRope[256];
3133
public static final LeafRope[] US_ASCII_SINGLE_BYTE_ROPES = new LeafRope[256];
3234
public static final LeafRope[] ASCII_8BIT_SINGLE_BYTE_ROPES = new LeafRope[256];
33-
static final Map<String, LeafRope> ROPE_CONSTANTS = new HashMap<>();
3435

3536
static {
3637
for (int i = 0; i < 128; i++) {
3738
final byte[] bytes = new byte[]{ (byte) i };
3839

39-
UTF8_SINGLE_BYTE_ROPES[i] = new AsciiOnlyLeafRope(bytes, UTF8Encoding.INSTANCE).computeHashCode();
40-
US_ASCII_SINGLE_BYTE_ROPES[i] = new AsciiOnlyLeafRope(bytes, USASCIIEncoding.INSTANCE).computeHashCode();
41-
ASCII_8BIT_SINGLE_BYTE_ROPES[i] = new AsciiOnlyLeafRope(bytes, ASCIIEncoding.INSTANCE).computeHashCode();
40+
UTF8_SINGLE_BYTE_ROPES[i] = withHashCode(new AsciiOnlyLeafRope(bytes, UTF8Encoding.INSTANCE));
41+
US_ASCII_SINGLE_BYTE_ROPES[i] = withHashCode(new AsciiOnlyLeafRope(bytes, USASCIIEncoding.INSTANCE));
42+
ASCII_8BIT_SINGLE_BYTE_ROPES[i] = withHashCode(new AsciiOnlyLeafRope(bytes, ASCIIEncoding.INSTANCE));
4243
}
4344

4445
for (int i = 128; i < 256; i++) {
4546
final byte[] bytes = new byte[]{ (byte) i };
4647

47-
UTF8_SINGLE_BYTE_ROPES[i] = new InvalidLeafRope(bytes, UTF8Encoding.INSTANCE, 1).computeHashCode();
48-
US_ASCII_SINGLE_BYTE_ROPES[i] = new InvalidLeafRope(bytes, USASCIIEncoding.INSTANCE, 1).computeHashCode();
49-
ASCII_8BIT_SINGLE_BYTE_ROPES[i] = new ValidLeafRope(bytes, ASCIIEncoding.INSTANCE, 1).computeHashCode();
48+
UTF8_SINGLE_BYTE_ROPES[i] = withHashCode(new InvalidLeafRope(bytes, UTF8Encoding.INSTANCE, 1));
49+
US_ASCII_SINGLE_BYTE_ROPES[i] = withHashCode(new InvalidLeafRope(bytes, USASCIIEncoding.INSTANCE, 1));
50+
ASCII_8BIT_SINGLE_BYTE_ROPES[i] = withHashCode(new ValidLeafRope(bytes, ASCIIEncoding.INSTANCE, 1));
5051
}
5152
}
5253

@@ -109,7 +110,7 @@ private static Rope ascii(String string) {
109110
return US_ASCII_SINGLE_BYTE_ROPES[string.charAt(0)];
110111
} else {
111112
final byte[] bytes = RopeOperations.encodeAsciiBytes(string);
112-
final LeafRope rope = new AsciiOnlyLeafRope(bytes, USASCIIEncoding.INSTANCE).computeHashCode();
113+
final LeafRope rope = withHashCode(new AsciiOnlyLeafRope(bytes, USASCIIEncoding.INSTANCE));
113114
final Rope existing = ROPE_CONSTANTS.putIfAbsent(string, rope);
114115
if (existing != null) {
115116
throw new AssertionError("Duplicate Rope in RopeConstants: " + existing);
@@ -126,4 +127,50 @@ public static LeafRope lookupUSASCII(String string) {
126127
}
127128
}
128129

130+
private static final LeafRope[] PADDED_NUMBERS = createPaddedNumbersTable();
131+
132+
private static LeafRope[] createPaddedNumbersTable() {
133+
final LeafRope[] table = new LeafRope[100];
134+
135+
for (int n = 0; n < table.length; n++) {
136+
table[n] = new AsciiOnlyLeafRope(
137+
new byte[]{ (byte) ('0' + n / 10), (byte) ('0' + n % 10) },
138+
UTF8Encoding.INSTANCE);
139+
}
140+
141+
return table;
142+
}
143+
144+
/*** Zero-padded numbers in the format %02d, between 00 and 99. */
145+
public static LeafRope paddedNumber(int n) {
146+
return PADDED_NUMBERS[n];
147+
}
148+
149+
private static final LeafRope[] PADDING_ZEROS = createPaddingZeroTable();
150+
151+
private static LeafRope[] createPaddingZeroTable() {
152+
final LeafRope[] table = new LeafRope[6];
153+
154+
for (int n = 0; n < table.length; n++) {
155+
final byte[] bytes = new byte[n];
156+
157+
for (int i = 0; i < bytes.length; i++) {
158+
bytes[i] = '0';
159+
}
160+
161+
table[n] = new AsciiOnlyLeafRope(bytes, UTF8Encoding.INSTANCE);
162+
}
163+
164+
return table;
165+
}
166+
167+
public static LeafRope paddingZeros(int n) {
168+
return PADDING_ZEROS[n];
169+
}
170+
171+
private static <T> T withHashCode(T object) {
172+
object.hashCode();
173+
return object;
174+
}
175+
129176
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -828,7 +828,7 @@ protected Object debugPrintRepeatingRope(RepeatingRope rope, int currentLevel, b
828828
final boolean bytesAreNull = rope.getRawBytes() == null;
829829

830830
System.err.println(String.format(
831-
"%s (%s; BN: %b; BL: %d; CL: %d; CR: %s; T: %d; D: %d; E: %s)",
831+
"%s (%s; BN: %b; BL: %d; CL: %d; CR: %s; T: %d; E: %s)",
832832
printString ? RopeOperations.escape(rope) : "<skipped>",
833833
rope.getClass().getSimpleName(),
834834
bytesAreNull,
@@ -852,7 +852,7 @@ protected Object debugPrintLazyInt(LazyIntRope rope, int currentLevel, boolean p
852852
final boolean bytesAreNull = rope.getRawBytes() == null;
853853

854854
System.err.println(String.format(
855-
"%s (%s; BN: %b; BL: %d; CL: %d; CR: %s; V: %d, D: %d; E: %s)",
855+
"%s (%s; BN: %b; BL: %d; CL: %d; CR: %s; V: %d, E: %s)",
856856
printString ? RopeOperations.escape(rope) : "<skipped>",
857857
rope.getClass().getSimpleName(),
858858
bytesAreNull,

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,7 @@ protected Object debugPrint(Object string, boolean printString,
7676
System.err.println("CR = Code Range");
7777
System.err.println("O = Byte Offset (SubstringRope only)");
7878
System.err.println("T = Times (RepeatingRope only)");
79-
System.err.println("D = Depth");
80-
System.err.println("LD = Left Depth (ConcatRope only)");
81-
System.err.println("RD = Right Depth (ConcatRope only)");
79+
System.err.println("V = Value (LazyIntRope only)");
8280
System.err.println("E = Encoding");
8381
System.err.println("P = Native Pointer (NativeRope only)");
8482
System.err.println("S = Native Size (NativeRope only)");

0 commit comments

Comments
 (0)