Details
-
Type:
Improvement
-
Status:
Resolved
-
Priority:
Major
-
Resolution: Fixed
-
Affects Version/s: JRuby 1.6RC1
-
Fix Version/s: None
-
Component/s: Core Classes/Modules
-
Labels:None
-
Number of attachments :
Description
In playing with ways to speed up JRuby to Java invocation, I added a cache for the toString result of a RubyString. RubyString normally causes a new String object to be created for every call. In many cases, these calls are repeatedly passed the same String object. By caching that String and flushing it on modification, we can end up with much better call performance.
Here's the perf difference:
BEFORE:
~/projects/jruby ➔ jruby -rbenchmark -e "10.times { puts Benchmark.measure { str = 'foo' * 10; dashe = Java::DashE; 1_000_000.times { dashe.foo(str) } } }"
1.170000 0.000000 1.170000 ( 1.058000)
0.695000 0.000000 0.695000 ( 0.695000)
0.694000 0.000000 0.694000 ( 0.694000)
0.691000 0.000000 0.691000 ( 0.691000)
0.695000 0.000000 0.695000 ( 0.696000)
0.685000 0.000000 0.685000 ( 0.685000)
0.690000 0.000000 0.690000 ( 0.690000)
0.725000 0.000000 0.725000 ( 0.725000)
0.725000 0.000000 0.725000 ( 0.725000)
0.692000 0.000000 0.692000 ( 0.692000)
AFTER:
~/projects/jruby ➔ jruby -rbenchmark -e "10.times { puts Benchmark.measure { str = 'foo' * 10; dashe = Java::DashE; 1_000_000.times { dashe.foo(str) } } }"
0.797000 0.000000 0.797000 ( 0.684000)
0.332000 0.000000 0.332000 ( 0.332000)
0.329000 0.000000 0.329000 ( 0.329000)
0.332000 0.000000 0.332000 ( 0.332000)
0.320000 0.000000 0.320000 ( 0.320000)
0.320000 0.000000 0.320000 ( 0.320000)
0.357000 0.000000 0.357000 ( 0.357000)
0.350000 0.000000 0.350000 ( 0.350000)
0.321000 0.000000 0.321000 ( 0.321000)
0.324000 0.000000 0.324000 ( 0.324000)
And here's the patch:
diff --git a/src/org/jruby/RubyString.java b/src/org/jruby/RubyString.java
index bc8377f..04ec6f3 100644
--- a/src/org/jruby/RubyString.java
+++ b/src/org/jruby/RubyString.java
@@ -126,6 +126,8 @@ public class RubyString extends RubyObject implements EncodingCapable {
private RString rstring;
+ private String lastString;
+
public static RubyClass createStringClass(Ruby runtime) {
RubyClass stringClass = runtime.defineClass("String", runtime.getObject(), STRING_ALLOCATOR);
runtime.setString(stringClass);
@@ -649,7 +651,19 @@ public class RubyString extends RubyObject implements EncodingCapable {
@Override
public String toString() {
- return value.toString();
+ String last = lastString;
+ if (last == null) {
+ try {
+ if (getRuntime().is1_9()) {
+ lastString = last = new String(value.getUnsafeBytes(), value.begin(), value.length(), getEncoding().toString());
+ } else {
+ lastString = last = RubyEncoding.decodeUTF8(value.getUnsafeBytes(), value.begin(), value.length());
+ }
+ } catch (UnsupportedEncodingException uee) {
+ lastString = last = value.toString();
+ }
+ }
+ return last;
}
/** rb_str_dup
@@ -731,6 +745,8 @@ public class RubyString extends RubyObject implements EncodingCapable {
if (!isTaint() && getRuntime().getSafeLevel() >= 4) {
throw getRuntime().newSecurityError("Insecure: can't modify string");
}
+
+ lastString = null;
}
private final void modifyCheck(byte[] b, int len) {
@@ -7292,17 +7308,7 @@ public class RubyString extends RubyObject implements EncodingCapable {
@Override
public Object toJava(Class target) {
if (target.isAssignableFrom(String.class)) {
- try {
- // 1.9 support for encodings
- // TODO: Fix charset use for JRUBY-4553
- if (getRuntime().is1_9()) {
- return new String(value.getUnsafeBytes(), value.begin(), value.length(), getEncoding().toString());
- }
-
- return RubyEncoding.decodeUTF8(value.getUnsafeBytes(), value.begin(), value.length());
- } catch (UnsupportedEncodingException uee) {
- return toString();
- }
+ return toString();
} else if (target.isAssignableFrom(ByteList.class)) {
return value;
} else {
Activity
Charles Oliver Nutter
made changes -
| Field | Original Value | New Value |
|---|---|---|
| Resolution | Fixed [ 1 ] | |
| Assignee | Charles Oliver Nutter [ headius ] | |
| Status | Open [ 1 ] | Resolved [ 5 ] |