Skip to content

Commit 5767b3e

Browse files
committed
Optimize rb_str_set_len()
* Avoid walking the characters, as this is called repeatedly during Zlib decompression and causes Bundler to be very slow when downloading the list of gems. * The CodeRange needs to be CR_VALID, otherwise zlib breaks. * A better solution would be to make CodeRange and character length lazy for NativeRope.
1 parent 89ddbd2 commit 5767b3e

File tree

2 files changed

+36
-16
lines changed

2 files changed

+36
-16
lines changed

src/main/java/org/truffleruby/cext/CExtNodes.java

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.oracle.truffle.api.profiles.ConditionProfile;
2929
import com.oracle.truffle.api.source.SourceSection;
3030
import org.jcodings.Encoding;
31+
import org.jcodings.specific.ASCIIEncoding;
3132
import org.jcodings.specific.UTF8Encoding;
3233
import org.truffleruby.Layouts;
3334
import org.truffleruby.RubyContext;
@@ -63,6 +64,7 @@
6364
import org.truffleruby.core.string.StringSupport;
6465
import org.truffleruby.interop.ToJavaStringNodeGen;
6566
import org.truffleruby.language.LexicalScope;
67+
import org.truffleruby.language.NotOptimizedWarningNode;
6668
import org.truffleruby.language.NotProvided;
6769
import org.truffleruby.language.RubyBaseNode;
6870
import org.truffleruby.language.RubyGuards;
@@ -443,15 +445,43 @@ public long capacity(DynamicObject string,
443445
@CoreMethod(names = "rb_str_set_len", onSingleton = true, required = 2, lowerFixnum = 2)
444446
public abstract static class RbStrSetLenNode extends CoreMethodArrayArgumentsNode {
445447

448+
@Child NotOptimizedWarningNode notOptimizedWarningNode;
449+
446450
@Specialization
447-
public DynamicObject strSetLen(DynamicObject string, int len,
448-
@Cached("create()") StringToNativeNode stringToNativeNode) {
451+
public DynamicObject strSetLen(DynamicObject string, int newByteLength,
452+
@Cached("create()") StringToNativeNode stringToNativeNode,
453+
@Cached("createBinaryProfile()") ConditionProfile binaryProfile) {
449454
final NativeRope nativeRope = stringToNativeNode.executeToNative(string);
450-
final NativeRope newNativeRope = nativeRope.withByteLength(len);
455+
final NativeRope newNativeRope;
456+
457+
if (binaryProfile.profile(nativeRope.getEncoding() == ASCIIEncoding.INSTANCE)) {
458+
// TODO (eregon, 17 Jan 2019): We use CR_VALID here as zlib relies on it. Computing
459+
// the coderange here on every call is too slow. A proper fix is to compute the
460+
// CodeRange lazily on NativeRope.
461+
newNativeRope = nativeRope.withByteLength(newByteLength, newByteLength, CodeRange.CR_VALID);
462+
} else {
463+
performanceWarn("calling rb_str_set_len() on non-binary string is not yet optimized");
464+
465+
final byte[] bytes = new byte[newByteLength];
466+
nativeRope.getNativePointer().readBytes(0, bytes, 0, newByteLength);
467+
final long packedLengthAndCodeRange = RopeOperations.calculateCodeRangeAndLength(nativeRope.getEncoding(), bytes, 0, newByteLength);
468+
final CodeRange codeRange = CodeRange.fromInt(StringSupport.unpackArg(packedLengthAndCodeRange));
469+
final int characterLength = StringSupport.unpackResult(packedLengthAndCodeRange);
470+
newNativeRope = nativeRope.withByteLength(newByteLength, characterLength, codeRange);
471+
}
472+
451473
StringOperations.setRope(string, newNativeRope);
452474
return string;
453475
}
454476

477+
private void performanceWarn(String message) {
478+
if (notOptimizedWarningNode == null) {
479+
CompilerDirectives.transferToInterpreterAndInvalidate();
480+
notOptimizedWarningNode = insert(new NotOptimizedWarningNode());
481+
}
482+
notOptimizedWarningNode.warn(message);
483+
}
484+
455485
}
456486

457487
@CoreMethod(names = "rb_str_resize", onSingleton = true, required = 2, lowerFixnum = 2)

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

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
import org.jcodings.Encoding;
1313
import org.truffleruby.core.FinalizationService;
14-
import org.truffleruby.core.string.StringSupport;
1514

1615
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
1716
import org.truffleruby.extra.ffi.Pointer;
@@ -44,18 +43,9 @@ private NativeRope(Pointer pointer, int byteLength, Encoding encoding, int chara
4443
this.pointer = pointer;
4544
}
4645

47-
@TruffleBoundary
48-
public NativeRope withByteLength(int newByteLength) {
49-
final byte[] bytes = new byte[newByteLength];
50-
pointer.readBytes(0, bytes, 0, newByteLength);
51-
52-
final long packedLengthAndCodeRange = RopeOperations.calculateCodeRangeAndLength(getEncoding(), bytes, 0, newByteLength);
53-
final CodeRange codeRange = CodeRange.fromInt(StringSupport.unpackArg(packedLengthAndCodeRange));
54-
final int characterLength = StringSupport.unpackResult(packedLengthAndCodeRange);
55-
56-
getNativePointer().writeByte(newByteLength, (byte) 0); // Like MRI
57-
58-
return new NativeRope(getNativePointer(), newByteLength, getEncoding(), characterLength, codeRange);
46+
public NativeRope withByteLength(int newByteLength, int characterLength, CodeRange codeRange) {
47+
pointer.writeByte(newByteLength, (byte) 0); // Like MRI
48+
return new NativeRope(pointer, newByteLength, getEncoding(), characterLength, codeRange);
5949
}
6050

6151
public NativeRope makeCopy(FinalizationService finalizationService) {

0 commit comments

Comments
 (0)