Skip to content

Commit

Permalink
[GR-56166] Remove inline caching in NegotiateCompatibleEncodingNode a…
Browse files Browse the repository at this point in the history
…nd NegotiateCompatibleStringEncodingNode nodes

PullRequest: truffleruby/4332
  • Loading branch information
andrykonchin committed Sep 25, 2024
2 parents 53c38f3 + c824668 commit a23be72
Show file tree
Hide file tree
Showing 11 changed files with 527 additions and 343 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Compatibility:

Performance:

* Optimize encoding negotiation for ASCII-compatible encodings (@eregon, @andrykonchin).

Changes:
* Inherit `Polyglot::ForeignException` from `StandardError` instead of `Exception` (#3620, @andrykonchin).

Expand Down
381 changes: 381 additions & 0 deletions spec/ruby/core/encoding/compatible_spec.rb

Large diffs are not rendered by default.

373 changes: 93 additions & 280 deletions src/main/java/org/truffleruby/core/encoding/EncodingNodes.java

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/main/java/org/truffleruby/core/encoding/Encodings.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,5 @@ public static RubyEncoding getBuiltInEncoding(String name) {

return null;
}

}
32 changes: 19 additions & 13 deletions src/main/java/org/truffleruby/core/string/StringHelperNodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,24 @@
public abstract class StringHelperNodes {

@TruffleBoundary
static Object trTransHelper(Node node, EncodingNodes.CheckEncodingNode checkEncodingNode, RubyString self,
static Object trTransHelper(Node node, EncodingNodes.CheckStringEncodingNode checkEncodingNode, RubyString self,
RubyStringLibrary libFromStr, Object fromStr,
RubyStringLibrary libToStr, Object toStr, boolean sFlag) {
final RubyEncoding e1 = checkEncodingNode.execute(node, self, fromStr);
final RubyEncoding e2 = checkEncodingNode.execute(node, self, toStr);
final RubyEncoding enc = e1 == e2 ? e1 : checkEncodingNode.execute(node, fromStr, toStr);

var selfTStringWithEnc = new ATStringWithEncoding(self.tstring, self.getEncodingUncached());
var fromStrTStringWithEnc = new ATStringWithEncoding(node, libFromStr, fromStr);
var toStrTStringWithEnc = new ATStringWithEncoding(node, libToStr, toStr);
final TruffleString ret = StringSupport.trTransHelper(selfTStringWithEnc, fromStrTStringWithEnc,
toStrTStringWithEnc, e1.jcoding, enc, sFlag, node);
var selfWithEnc = new ATStringWithEncoding(self.tstring, self.getEncodingUncached());
var fromWithEnc = new ATStringWithEncoding(node, libFromStr, fromStr);
var toWithEnc = new ATStringWithEncoding(node, libToStr, toStr);

final RubyEncoding e1 = checkEncodingNode.execute(node, selfWithEnc.tstring, selfWithEnc.encoding,
fromWithEnc.tstring, fromWithEnc.encoding);
final RubyEncoding e2 = checkEncodingNode.execute(node, selfWithEnc.tstring, selfWithEnc.encoding,
toWithEnc.tstring, toWithEnc.encoding);
final RubyEncoding enc = e1 == e2
? e1
: checkEncodingNode.execute(node, fromWithEnc.tstring, fromWithEnc.encoding, toWithEnc.tstring,
toWithEnc.encoding);

final TruffleString ret = StringSupport.trTransHelper(selfWithEnc, fromWithEnc,
toWithEnc, e1.jcoding, enc, sFlag, node);
if (ret == null) {
return Nil.INSTANCE;
}
Expand Down Expand Up @@ -245,11 +251,11 @@ protected boolean[] squeeze() {

protected static RubyEncoding findEncoding(Node node, AbstractTruffleString tstring, RubyEncoding encoding,
TStringWithEncoding[] tstringsWithEncs, EncodingNodes.CheckStringEncodingNode checkEncodingNode) {
RubyEncoding enc = checkEncodingNode.executeCheckEncoding(node, tstring, encoding,
RubyEncoding enc = checkEncodingNode.execute(node, tstring, encoding,
tstringsWithEncs[0].tstring,
tstringsWithEncs[0].encoding);
for (int i = 1; i < tstringsWithEncs.length; i++) {
enc = checkEncodingNode.executeCheckEncoding(node, tstring, encoding, tstringsWithEncs[i].tstring,
enc = checkEncodingNode.execute(node, tstring, encoding, tstringsWithEncs[i].tstring,
tstringsWithEncs[i].encoding);
}
return enc;
Expand Down Expand Up @@ -613,7 +619,7 @@ static RubyString stringAppend(Node node, Object string, Object other,
var right = libOther.getTString(node, other);
var rightEncoding = libOther.getEncoding(node, other);

final RubyEncoding compatibleEncoding = checkEncodingNode.executeCheckEncoding(node, left, leftEncoding,
final RubyEncoding compatibleEncoding = checkEncodingNode.execute(node, left, leftEncoding,
right, rightEncoding);

var result = concatNode.execute(left, right, compatibleEncoding.tencoding, true);
Expand Down
45 changes: 25 additions & 20 deletions src/main/java/org/truffleruby/core/string/StringNodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,9 @@
import org.truffleruby.core.cast.ToIntNode;
import org.truffleruby.core.cast.ToLongNode;
import org.truffleruby.core.cast.ToStrNode;
import org.truffleruby.core.encoding.EncodingNodes.CheckStringEncodingNode;
import org.truffleruby.core.encoding.EncodingNodes.NegotiateCompatibleStringEncodingNode;
import org.truffleruby.core.encoding.IsCharacterHeadNode;
import org.truffleruby.core.encoding.EncodingNodes.CheckEncodingNode;
import org.truffleruby.core.encoding.EncodingNodes.GetActualEncodingNode;
import org.truffleruby.core.encoding.Encodings;
import org.truffleruby.core.encoding.RubyEncoding;
Expand Down Expand Up @@ -2363,8 +2363,8 @@ Object squeezeBangZeroArgs(RubyString string, Object[] args,
}

@Specialization(guards = { "!string.tstring.isEmpty()", "!noArguments(args)" })
static Object squeezeBang(VirtualFrame frame, RubyString string, Object[] args,
@Cached CheckEncodingNode checkEncodingNode,
static Object squeezeBang(RubyString string, Object[] args,
@Cached CheckStringEncodingNode checkEncodingNode,
@Cached ToStrNode toStrNode,
@Bind("this") Node node) {
// Taken from org.jruby.RubyString#squeeze_bang19.
Expand All @@ -2380,14 +2380,15 @@ static Object squeezeBang(VirtualFrame frame, RubyString string, Object[] args,

@TruffleBoundary
private static Object performSqueezeBang(Node node, RubyString string, Object[] otherStrings,
CheckEncodingNode checkEncodingNode) {
CheckStringEncodingNode checkEncodingNode) {

final TStringBuilder buffer = TStringBuilder.create(string);

Object otherStr = otherStrings[0];
var otherTString = RubyStringLibrary.getUncached().getTString(node, otherStr);
var otherEncoding = RubyStringLibrary.getUncached().getEncoding(node, otherStr);
RubyEncoding enc = checkEncodingNode.execute(node, string, otherStr);
RubyEncoding enc = checkEncodingNode.execute(node, string.tstring, string.getEncodingUncached(),
otherTString, otherEncoding);
final boolean squeeze[] = new boolean[StringSupport.TRANS_SIZE + 1];

boolean singlebyte = TStringUtils.isSingleByteOptimizable(string.tstring, string.getEncodingUncached()) &&
Expand All @@ -2410,7 +2411,8 @@ private static Object performSqueezeBang(Node node, RubyString string, Object[]
otherStr = otherStrings[i];
otherTString = RubyStringLibrary.getUncached().getTString(node, otherStr);
otherEncoding = RubyStringLibrary.getUncached().getEncoding(node, otherStr);
enc = checkEncodingNode.execute(node, string, otherStr);
enc = checkEncodingNode.execute(node, string.tstring, string.getEncodingUncached(), otherTString,
otherEncoding);
singlebyte = singlebyte && TStringUtils.isSingleByteOptimizable(otherTString, otherEncoding);
tables = StringSupport.trSetupTable(otherTString, otherEncoding, squeeze, tables, false, enc.jcoding,
node);
Expand Down Expand Up @@ -2733,7 +2735,7 @@ static Object trBangToEmpty(Node node, RubyString self, Object fromStr, Object t
"!libToStr.getTString(node, toStr).isEmpty()" },
limit = "1")
static Object trBangNoEmpty(Node node, RubyString self, Object fromStr, Object toStr,
@Cached CheckEncodingNode checkEncodingNode,
@Cached CheckStringEncodingNode checkEncodingNode,
@Cached @Exclusive RubyStringLibrary libFromStr,
@Cached @Exclusive RubyStringLibrary libToStr) {
return StringHelperNodes.trTransHelper(node, checkEncodingNode, self, libFromStr, fromStr, libToStr, toStr,
Expand Down Expand Up @@ -2763,7 +2765,7 @@ static Object trSBang(RubyString self, Object fromStr, Object toStr,
@Bind("this") Node node,
@Bind("fromStrNode.execute(node, fromStr)") Object fromStrAsString,
@Bind("toStrNode.execute(node, toStr)") Object toStrAsString,
@Cached CheckEncodingNode checkEncodingNode,
@Cached CheckStringEncodingNode checkEncodingNode,
@Cached DeleteBangNode deleteBangNode,
@Cached RubyStringLibrary libFromStr,
@Cached RubyStringLibrary libToStr) {
Expand Down Expand Up @@ -3777,17 +3779,20 @@ static Object findStringByteIndex(Object rubyString, Object rubyPattern, int byt
@Bind("this") Node node,
@Cached @Exclusive RubyStringLibrary libString,
@Cached @Exclusive RubyStringLibrary libPattern,
@Cached CheckEncodingNode checkEncodingNode,
@Cached CheckStringEncodingNode checkEncodingNode,
@Cached TruffleString.ByteIndexOfStringNode indexOfStringNode,
@Cached InlinedConditionProfile offsetTooLargeProfile,
@Cached InlinedConditionProfile notFoundProfile,
@Bind("libPattern.getTString(node, rubyPattern)") AbstractTruffleString patternTString) {
assert byteOffset >= 0;

var compatibleEncoding = checkEncodingNode.execute(node, rubyString, rubyPattern);

var string = libString.getTString(node, rubyString);
int stringByteLength = string.byteLength(libString.getTEncoding(node, rubyString));
var encoding = libString.getEncoding(node, rubyString);
var patternEncoding = libPattern.getEncoding(node, rubyPattern);

var compatibleEncoding = checkEncodingNode.execute(node, string, encoding, patternTString, patternEncoding);

int stringByteLength = string.byteLength(encoding.tencoding);

if (offsetTooLargeProfile.profile(node, byteOffset >= stringByteLength)) {
return nil;
Expand Down Expand Up @@ -4040,23 +4045,23 @@ public abstract static class StringRindexPrimitiveNode extends PrimitiveArrayArg
Object stringRindex(Object rubyString, Object rubyPattern, int byteOffset,
@Cached RubyStringLibrary libPattern,
@Cached RubyStringLibrary libString,
@Cached CheckEncodingNode checkEncodingNode,
@Cached CheckStringEncodingNode checkEncodingNode,
@Cached TruffleString.LastByteIndexOfStringNode lastByteIndexOfStringNode,
@Cached InlinedBranchProfile startOutOfBoundsProfile,
@Cached InlinedBranchProfile startTooCloseToEndProfile,
@Cached InlinedBranchProfile noMatchProfile) {
assert byteOffset >= 0;

// Throw an exception if the encodings are not compatible.
var compatibleEncoding = checkEncodingNode.execute(this, rubyString, rubyPattern);

var string = libString.getTString(this, rubyString);
var stringEncoding = libString.getEncoding(this, rubyString).tencoding;
int stringByteLength = string.byteLength(stringEncoding);
var stringEncoding = libString.getEncoding(this, rubyString);
int stringByteLength = string.byteLength(stringEncoding.tencoding);

var pattern = libPattern.getTString(this, rubyPattern);
var patternEncoding = libPattern.getEncoding(this, rubyPattern).tencoding;
int patternByteLength = pattern.byteLength(patternEncoding);
var patternEncoding = libPattern.getEncoding(this, rubyPattern);
int patternByteLength = pattern.byteLength(patternEncoding.tencoding);

// Throw an exception if the encodings are not compatible.
var compatibleEncoding = checkEncodingNode.execute(this, string, stringEncoding, pattern, patternEncoding);

int normalizedStart = byteOffset;

Expand Down
13 changes: 0 additions & 13 deletions src/main/java/org/truffleruby/options/LanguageOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,6 @@ public final class LanguageOptions {
public final int UNPACK_CACHE;
/** --eval-cache=DEFAULT_CACHE */
public final int EVAL_CACHE;
/** --encoding-compatible-query-cache=DEFAULT_CACHE */
public final int ENCODING_COMPATIBLE_QUERY_CACHE;
/** --encoding-loaded-classes-cache=DEFAULT_CACHE */
public final int ENCODING_LOADED_CLASSES_CACHE;
/** --interop-convert-cache=DEFAULT_CACHE */
Expand Down Expand Up @@ -163,7 +161,6 @@ public LanguageOptions(Env env, OptionValues options, boolean singleContext) {
PACK_CACHE = options.hasBeenSet(OptionsCatalog.PACK_CACHE_KEY) ? options.get(OptionsCatalog.PACK_CACHE_KEY) : DEFAULT_CACHE;
UNPACK_CACHE = options.hasBeenSet(OptionsCatalog.UNPACK_CACHE_KEY) ? options.get(OptionsCatalog.UNPACK_CACHE_KEY) : DEFAULT_CACHE;
EVAL_CACHE = options.hasBeenSet(OptionsCatalog.EVAL_CACHE_KEY) ? options.get(OptionsCatalog.EVAL_CACHE_KEY) : DEFAULT_CACHE;
ENCODING_COMPATIBLE_QUERY_CACHE = options.hasBeenSet(OptionsCatalog.ENCODING_COMPATIBLE_QUERY_CACHE_KEY) ? options.get(OptionsCatalog.ENCODING_COMPATIBLE_QUERY_CACHE_KEY) : DEFAULT_CACHE;
ENCODING_LOADED_CLASSES_CACHE = options.hasBeenSet(OptionsCatalog.ENCODING_LOADED_CLASSES_CACHE_KEY) ? options.get(OptionsCatalog.ENCODING_LOADED_CLASSES_CACHE_KEY) : DEFAULT_CACHE;
INTEROP_CONVERT_CACHE = options.hasBeenSet(OptionsCatalog.INTEROP_CONVERT_CACHE_KEY) ? options.get(OptionsCatalog.INTEROP_CONVERT_CACHE_KEY) : DEFAULT_CACHE;
TIME_FORMAT_CACHE = options.hasBeenSet(OptionsCatalog.TIME_FORMAT_CACHE_KEY) ? options.get(OptionsCatalog.TIME_FORMAT_CACHE_KEY) : DEFAULT_CACHE;
Expand Down Expand Up @@ -252,8 +249,6 @@ public Object fromDescriptor(OptionDescriptor descriptor) {
return UNPACK_CACHE;
case "ruby.eval-cache":
return EVAL_CACHE;
case "ruby.encoding-compatible-query-cache":
return ENCODING_COMPATIBLE_QUERY_CACHE;
case "ruby.encoding-loaded-classes-cache":
return ENCODING_LOADED_CLASSES_CACHE;
case "ruby.interop-convert-cache":
Expand Down Expand Up @@ -332,7 +327,6 @@ public static boolean areOptionsCompatible(OptionValues one, OptionValues two) {
one.get(OptionsCatalog.PACK_CACHE_KEY).equals(two.get(OptionsCatalog.PACK_CACHE_KEY)) &&
one.get(OptionsCatalog.UNPACK_CACHE_KEY).equals(two.get(OptionsCatalog.UNPACK_CACHE_KEY)) &&
one.get(OptionsCatalog.EVAL_CACHE_KEY).equals(two.get(OptionsCatalog.EVAL_CACHE_KEY)) &&
one.get(OptionsCatalog.ENCODING_COMPATIBLE_QUERY_CACHE_KEY).equals(two.get(OptionsCatalog.ENCODING_COMPATIBLE_QUERY_CACHE_KEY)) &&
one.get(OptionsCatalog.ENCODING_LOADED_CLASSES_CACHE_KEY).equals(two.get(OptionsCatalog.ENCODING_LOADED_CLASSES_CACHE_KEY)) &&
one.get(OptionsCatalog.INTEROP_CONVERT_CACHE_KEY).equals(two.get(OptionsCatalog.INTEROP_CONVERT_CACHE_KEY)) &&
one.get(OptionsCatalog.TIME_FORMAT_CACHE_KEY).equals(two.get(OptionsCatalog.TIME_FORMAT_CACHE_KEY)) &&
Expand Down Expand Up @@ -583,13 +577,6 @@ public static boolean areOptionsCompatibleOrLog(TruffleLogger logger, LanguageOp
return false;
}

oldValue = oldOptions.ENCODING_COMPATIBLE_QUERY_CACHE;
newValue = newOptions.ENCODING_COMPATIBLE_QUERY_CACHE;
if (!newValue.equals(oldValue)) {
logger.fine("not reusing pre-initialized context: --encoding-compatible-query-cache differs, was: " + oldValue + " and is now: " + newValue);
return false;
}

oldValue = oldOptions.ENCODING_LOADED_CLASSES_CACHE;
newValue = newOptions.ENCODING_LOADED_CLASSES_CACHE;
if (!newValue.equals(oldValue)) {
Expand Down
5 changes: 4 additions & 1 deletion src/main/ruby/truffleruby/core/dir.rb
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,10 @@ def glob(pattern, flags = 0, base: nil, sort: true, &block)

patterns.each do |pat|
pat = Truffle::Type.coerce_to_path pat
enc = Primitive.encoding_ensure_compatible pat, Encoding::US_ASCII
enc = Primitive.encoding_compatible? pat, Encoding::US_ASCII
unless enc
raise Encoding::CompatibilityError, "incompatible character encodings: #{pat.encoding.name} and US-ASCII"
end
Dir::Glob.glob normalized_base, pat, flags, matches

total = matches.size
Expand Down
4 changes: 2 additions & 2 deletions src/main/ruby/truffleruby/core/string.rb
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ def upto(stop, exclusive = false)
stop = StringValue(stop)

if stop.bytesize == 1 && bytesize == 1 && self.ascii_only? && stop.ascii_only?
enc = Primitive.encoding_ensure_compatible(self.encoding, stop.encoding)
enc = Primitive.encoding_ensure_compatible_str(self, stop)

return self if self > stop
after_stop = stop.getbyte(0) + (exclusive ? 0 : 1)
Expand All @@ -616,7 +616,7 @@ def upto(stop, exclusive = false)
end
else
unless stop.size < size
Primitive.encoding_ensure_compatible(self.encoding, stop.encoding)
Primitive.encoding_ensure_compatible_str(self, stop)

after_stop = exclusive ? stop : stop.succ
current = self
Expand Down
2 changes: 0 additions & 2 deletions src/options.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ LANGUAGE_OPTIONS:
- PACK_CACHE
- UNPACK_CACHE
- EVAL_CACHE
- ENCODING_COMPATIBLE_QUERY_CACHE
- ENCODING_LOADED_CLASSES_CACHE
- INTEROP_CONVERT_CACHE
- TIME_FORMAT_CACHE
Expand Down Expand Up @@ -212,7 +211,6 @@ INTERNAL: # Options for debugging the TruffleRuby implementation
PACK_CACHE: [pack-cache, integer, DEFAULT_CACHE, Array#pack cache size]
UNPACK_CACHE: [unpack-cache, integer, DEFAULT_CACHE, String#unpack cache size]
EVAL_CACHE: [eval-cache, integer, DEFAULT_CACHE, eval cache size]
ENCODING_COMPATIBLE_QUERY_CACHE: [encoding-compatible-query-cache, integer, DEFAULT_CACHE, 'Encoding.compatible? cache size']
ENCODING_LOADED_CLASSES_CACHE: [encoding-loaded-classes-cache, integer, DEFAULT_CACHE, Cache size of encoding operations based on anticipated number of total active encodings]
INTEROP_CONVERT_CACHE: [interop-convert-cache, integer, DEFAULT_CACHE, Cache size for converting values for interop]
TIME_FORMAT_CACHE: [time-format-cache, integer, DEFAULT_CACHE, Cache size for parsed time format specifiers]
Expand Down
Loading

0 comments on commit a23be72

Please sign in to comment.