Skip to content

Commit

Permalink
Optimize retrieval of method/callee name from compound
Browse files Browse the repository at this point in the history
Prior to this patch the compound name passed on the stack from an
AliasMethod was repeatedly parsed and split before acquiring the
associated symbol, leading to wasteful allocation of additional
String objects. The change here adds a new map to the symbol table
that tracks the two symbols associated with a compound name, so
that only a single lookup is needed and no allocation happens.
  • Loading branch information
headius committed Mar 28, 2024
1 parent 173110f commit 4d4f7f5
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 7 deletions.
27 changes: 27 additions & 0 deletions core/src/main/java/org/jruby/RubySymbol.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@
import org.jruby.util.TypeConverter;

import java.lang.ref.WeakReference;
import java.util.AbstractMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

import static org.jruby.util.RubyStringBuilder.str;
Expand Down Expand Up @@ -346,6 +349,14 @@ public static RubySymbol newHardSymbol(Ruby runtime, String name) {
return runtime.getSymbolTable().getSymbol(name, true);
}

public static RubySymbol newMethodSymbolFromCompound(Ruby runtime, String compoundName) {
return runtime.getSymbolTable().getCompoundSymbol(compoundName).getKey();
}

public static RubySymbol newCalleeSymbolFromCompound(Ruby runtime, String compoundName) {
return runtime.getSymbolTable().getCompoundSymbol(compoundName).getValue();
}

/**
* Return the symbol in the symbol table if it exists, null otherwise.
* This method will not create the symbol if it does not exist.
Expand Down Expand Up @@ -979,6 +990,7 @@ public static final class SymbolTable {

private final ReentrantLock tableLock = new ReentrantLock();
private volatile SymbolEntry[] symbolTable;
private Map<String, AbstractMap.SimpleImmutableEntry<RubySymbol, RubySymbol>> compoundSymbolTable = new ConcurrentHashMap<>();
private int size;
private int threshold;
private final float loadFactor;
Expand Down Expand Up @@ -1093,6 +1105,21 @@ public RubySymbol getSymbol(ByteList bytes, ObjBooleanConsumer<RubySymbol> handl
return symbol;
}

/**
* Get a pair of symbols associated with the given compound method name, used by aliases to pass both the callee
* name and the original method name on the stack. This avoids re-parsing the compoundName and constructing new
* strings every time __method__ or __callee__ are used in an aliased call.
*
* @param compoundName the compound name used for a combination of alias and method
* @return a Map.Entry representing the __method__ and __callee__ symbols for that compound name as key and value
*/
public Map.Entry<RubySymbol, RubySymbol> getCompoundSymbol(String compoundName) {
return compoundSymbolTable.computeIfAbsent(compoundName, (cname) ->
new AbstractMap.SimpleImmutableEntry(
getSymbol(Helpers.getSuperNameFromCompositeName(cname), true),
getSymbol(Helpers.getCalleeNameFromCompositeName(cname), true)));
}

private RubySymbol findSymbol(ByteList bytes, int hash, boolean hard) {
RubySymbol symbol = null;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package org.jruby.ir.instructions;

import org.jruby.RubySymbol;
import org.jruby.ir.IRVisitor;
import org.jruby.ir.Operation;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.persistence.IRReaderDecoder;
import org.jruby.ir.persistence.IRWriterEncoder;
import org.jruby.ir.transformations.inlining.CloneInfo;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.CacheEntry;
Expand Down Expand Up @@ -56,10 +56,9 @@ public IRubyObject getFrameName(ThreadContext context, IRubyObject self, String
return frameNameSite.call(context, self, self);
}

return context.runtime.newSymbol(
callee ?
Helpers.getSuperNameFromCompositeName(compositeName) :
Helpers.getCalleeNameFromCompositeName(compositeName));
return callee ?
RubySymbol.newCalleeSymbolFromCompound(context.runtime, compositeName):
RubySymbol.newMethodSymbolFromCompound(context.runtime, compositeName);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.jruby.ir.targets.indy;

import com.headius.invokebinder.Binder;
import org.jruby.RubySymbol;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
Expand Down Expand Up @@ -69,10 +70,10 @@ public IRubyObject frameNameFallback(String name, ThreadContext context, IRubyOb
}

public static IRubyObject __callee__(ThreadContext context, String frameName) {
return context.runtime.newSymbol(Helpers.getCalleeNameFromCompositeName(frameName));
return RubySymbol.newCalleeSymbolFromCompound(context.runtime, frameName);
}

public static IRubyObject __method__(ThreadContext context, String frameName) {
return context.runtime.newSymbol(Helpers.getSuperNameFromCompositeName(frameName));
return RubySymbol.newMethodSymbolFromCompound(context.runtime, frameName);
}
}

0 comments on commit 4d4f7f5

Please sign in to comment.