Skip to content

Commit a91eba5

Browse files
authored
Merge pull request #5814 from jruby/ops-regression
fix custom call-site performance regression
2 parents c38fd18 + e2db5b1 commit a91eba5

22 files changed

+1144
-235
lines changed

core/src/main/java/org/jruby/ext/ffi/AutoPointer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public final IRubyObject initialize(ThreadContext context, IRubyObject pointerAr
9494
ClassData classData = (ClassData) ffiHandle;
9595

9696
// If no release method is defined, then memory leaks will result.
97-
DynamicMethod releaseMethod = classData.releaseCallSite.retrieveCache(getMetaClass().getMetaClass(), classData.releaseCallSite.getMethodName()).method;
97+
DynamicMethod releaseMethod = classData.releaseCallSite.retrieveCache((IRubyObject) getMetaClass()).method;
9898
if (releaseMethod.isUndefined()) {
9999
throw runtime.newRuntimeError("release method undefined");
100100

@@ -130,7 +130,7 @@ public final IRubyObject initialize(ThreadContext context, IRubyObject pointerAr
130130
}
131131

132132
ReleaserData releaserData = (ReleaserData) ffiHandle;
133-
DynamicMethod releaseMethod = releaserData.releaseCallSite.retrieveCache(releaser.getMetaClass(), releaserData.releaseCallSite.getMethodName()).method;
133+
DynamicMethod releaseMethod = releaserData.releaseCallSite.retrieveCache(releaser).method;
134134
// If no release method is defined, then memory leaks will result.
135135
if (releaseMethod.isUndefined()) {
136136
throw context.runtime.newRuntimeError("call method undefined");

core/src/main/java/org/jruby/ext/ffi/jffi/JITRuntime.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ private static MemoryIO convertToStringMemoryIO(IRubyObject parameter, ThreadCon
499499
} else if (parameter instanceof RubyNil) {
500500
return NilPointerParameterStrategy.NullMemoryIO.INSTANCE;
501501

502-
} else if (!(conversionMethod = callSite.retrieveCache(parameter.getMetaClass(), callSite.getMethodName()).method).isUndefined()) {
502+
} else if (!(conversionMethod = callSite.retrieveCache(parameter).method).isUndefined()) {
503503
IRubyObject convertedParameter = conversionMethod.call(context, parameter, parameter.getMetaClass(), callSite.getMethodName(), Block.NULL_BLOCK);
504504
if (convertedParameter instanceof RubyString) {
505505
return StringParameterStrategy.getMemoryIO((RubyString) convertedParameter, isDirect, checkStringSafety);
@@ -573,7 +573,7 @@ public static MemoryIO convertToPointerMemoryIO(ThreadContext context, IRubyObje
573573
}
574574

575575
public static DynamicMethod getConversionMethod(IRubyObject parameter, CachingCallSite callSite) {
576-
DynamicMethod method = callSite.retrieveCache(parameter.getMetaClass(), callSite.getMethodName()).method;
576+
DynamicMethod method = callSite.retrieveCache(parameter).method;
577577
if (method.isUndefined()) {
578578
throw parameter.getRuntime().newTypeError("cannot convert parameter of type " + parameter.getMetaClass()
579579
+ " to native pointer; does not respond to :" + callSite.getMethodName());

core/src/main/java/org/jruby/ext/ffi/jffi/NativeCallbackFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ private synchronized Pointer getCallbackPointer(IRubyObject callable, CachingCal
6565
}
6666

6767
NativeCallbackPointer newCallback(IRubyObject callable, CachingCallSite callSite) {
68-
if (callSite.retrieveCache(callable.getMetaClass(), callSite.getMethodName()).method.isUndefined()) {
68+
if (callSite.retrieveCache(callable).method.isUndefined()) {
6969
throw runtime.newArgumentError("callback does not respond to :" + callSite.getMethodName());
7070
}
7171

core/src/main/java/org/jruby/runtime/callsite/BimorphicCallSite.java

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import org.jruby.RubyClass;
44
import org.jruby.runtime.CallType;
5+
import org.jruby.runtime.builtin.IRubyObject;
6+
7+
import static org.jruby.RubyBasicObject.getMetaClass;
58

69
/**
710
* A bi-morphic call-site.
@@ -20,36 +23,31 @@ public final CacheEntry getSecondaryCache() {
2023
return secondaryCache;
2124
}
2225

23-
public final CacheEntry retrieveSecondaryCache(RubyClass selfType) {
24-
// This must be retrieved *once* to avoid racing with other threads.
25-
CacheEntry cache = this.secondaryCache;
26-
if (cache.typeOk(selfType)) {
27-
return cache;
28-
}
29-
return cacheAndGetSecondary(selfType, methodName);
26+
protected CacheEntry setSecondaryCache(CacheEntry entry, IRubyObject self) {
27+
return secondaryCache = entry;
3028
}
3129

32-
public final CacheEntry retrieveSecondaryCache(RubyClass selfType, String methodName) {
33-
// This must be retrieved *once* to avoid racing with other threads.
30+
public final CacheEntry retrieveSecondaryCache(IRubyObject self) {
31+
RubyClass selfType = getMetaClass(self);
3432
CacheEntry cache = this.secondaryCache;
3533
if (cache.typeOk(selfType)) {
3634
return cache;
3735
}
38-
return cacheAndGetSecondary(selfType, methodName);
36+
return cacheAndGetSecondary(self, selfType, methodName);
3937
}
4038

41-
public final boolean isSecondaryBuiltin(RubyClass selfType) {
42-
// This must be retrieved *once* to avoid racing with other threads.
39+
public boolean isSecondaryBuiltin(IRubyObject self) {
40+
RubyClass selfType = getMetaClass(self);
4341
CacheEntry cache = this.secondaryCache;
4442
if (cache.typeOk(selfType)) {
4543
return cache.method.isBuiltin();
4644
}
47-
return cacheAndGetSecondary(selfType, methodName).method.isBuiltin(); // false for method.isUndefined()
45+
return cacheAndGetSecondary(self, selfType, methodName).method.isBuiltin(); // false for method.isUndefined()
4846
}
4947

50-
private CacheEntry cacheAndGetSecondary(RubyClass selfType, String methodName) {
48+
private CacheEntry cacheAndGetSecondary(IRubyObject self, RubyClass selfType, String methodName) {
5149
CacheEntry entry = selfType.searchWithCache(methodName);
52-
if (!entry.method.isUndefined()) this.secondaryCache = entry;
50+
if (!entry.method.isUndefined()) entry = setSecondaryCache(entry, self);
5351
return entry;
5452
}
5553

core/src/main/java/org/jruby/runtime/callsite/BitAndCallSite.java

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,59 @@
33
import org.jruby.RubyFixnum;
44
import org.jruby.runtime.ThreadContext;
55
import org.jruby.runtime.builtin.IRubyObject;
6+
import static org.jruby.RubyBasicObject.getMetaClass;
67

78
public class BitAndCallSite extends MonomorphicCallSite {
89

910
public BitAndCallSite() {
1011
super("&");
1112
}
1213

13-
public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, long fixnum) {
14-
if (self instanceof RubyFixnum && isBuiltin(((RubyFixnum) self).getMetaClass())) {
15-
return ((RubyFixnum) self).op_and(context, fixnum);
14+
@Override
15+
public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, long arg1) {
16+
if (self instanceof RubyFixnum) {
17+
CacheEntry cache = this.cache;
18+
if (cache instanceof FixnumEntry && cache.typeOk(getMetaClass(self))) {
19+
return ((RubyFixnum) self).op_and(context, arg1);
20+
}
21+
}
22+
return super.call(context, caller, self, arg1);
23+
}
24+
25+
@Override
26+
public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject arg1) {
27+
if (self instanceof RubyFixnum) {
28+
CacheEntry cache = this.cache;
29+
if (cache instanceof FixnumEntry && cache.typeOk(getMetaClass(self))) {
30+
return ((RubyFixnum) self).op_and(context, arg1);
31+
}
1632
}
17-
return super.call(context, caller, self, fixnum);
33+
return super.call(context, caller, self, arg1);
1834
}
1935

2036
@Override
21-
public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject arg) {
22-
if (self instanceof RubyFixnum && isBuiltin(((RubyFixnum) self).getMetaClass())) {
23-
return ((RubyFixnum) self).op_and(context, arg);
37+
protected CacheEntry setCache(final CacheEntry entry, final IRubyObject self) {
38+
if (self instanceof RubyFixnum && entry.method.isBuiltin()) {
39+
return cache = new FixnumEntry(entry); // tagged entry - do isBuiltin check once
2440
}
25-
return super.call(context, caller, self, arg);
41+
return cache = entry;
2642
}
43+
44+
@Override
45+
public boolean isBuiltin(final IRubyObject self) {
46+
if (self instanceof RubyFixnum) {
47+
CacheEntry cache = this.cache;
48+
if (cache.typeOk(getMetaClass(self))) return cache instanceof FixnumEntry;
49+
}
50+
return super.isBuiltin(self);
51+
}
52+
53+
private static class FixnumEntry extends CacheEntry {
54+
55+
FixnumEntry(CacheEntry entry) {
56+
super(entry.method, entry.sourceModule, entry.token);
57+
}
58+
59+
}
60+
2761
}

core/src/main/java/org/jruby/runtime/callsite/BitOrCallSite.java

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,59 @@
33
import org.jruby.RubyFixnum;
44
import org.jruby.runtime.ThreadContext;
55
import org.jruby.runtime.builtin.IRubyObject;
6+
import static org.jruby.RubyBasicObject.getMetaClass;
67

78
public class BitOrCallSite extends MonomorphicCallSite {
89

910
public BitOrCallSite() {
1011
super("|");
1112
}
1213

13-
public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, long fixnum) {
14-
if (self instanceof RubyFixnum && isBuiltin(((RubyFixnum) self).getMetaClass())) {
15-
return ((RubyFixnum) self).op_or(context, fixnum);
14+
@Override
15+
public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, long arg1) {
16+
if (self instanceof RubyFixnum) {
17+
CacheEntry cache = this.cache;
18+
if (cache instanceof FixnumEntry && cache.typeOk(getMetaClass(self))) {
19+
return ((RubyFixnum) self).op_or(context, arg1);
20+
}
21+
}
22+
return super.call(context, caller, self, arg1);
23+
}
24+
25+
@Override
26+
public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject arg1) {
27+
if (self instanceof RubyFixnum) {
28+
CacheEntry cache = this.cache;
29+
if (cache instanceof FixnumEntry && cache.typeOk(getMetaClass(self))) {
30+
return ((RubyFixnum) self).op_or(context, arg1);
31+
}
1632
}
17-
return super.call(context, caller, self, fixnum);
33+
return super.call(context, caller, self, arg1);
1834
}
1935

2036
@Override
21-
public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject arg) {
22-
if (self instanceof RubyFixnum && isBuiltin(((RubyFixnum) self).getMetaClass())) {
23-
return ((RubyFixnum) self).op_or(context, arg);
37+
protected CacheEntry setCache(final CacheEntry entry, final IRubyObject self) {
38+
if (self instanceof RubyFixnum && entry.method.isBuiltin()) {
39+
return cache = new FixnumEntry(entry); // tagged entry - do isBuiltin check once
2440
}
25-
return super.call(context, caller, self, arg);
41+
return cache = entry;
2642
}
43+
44+
@Override
45+
public boolean isBuiltin(final IRubyObject self) {
46+
if (self instanceof RubyFixnum) {
47+
CacheEntry cache = this.cache;
48+
if (cache.typeOk(getMetaClass(self))) return cache instanceof FixnumEntry;
49+
}
50+
return super.isBuiltin(self);
51+
}
52+
53+
private static class FixnumEntry extends CacheEntry {
54+
55+
FixnumEntry(CacheEntry entry) {
56+
super(entry.method, entry.sourceModule, entry.token);
57+
}
58+
59+
}
60+
2761
}

0 commit comments

Comments
 (0)