Skip to content

Commit 0497910

Browse files
committed
Provide concrete-only traversal for Class#subclasses
For classes with many singletons, the Class#subclasses walk will cost significantly more than is warranted for the few concrete classes it will return. This can be eliminated by tracking whether each entry is concrete and adding a separate traversal link to the next concrete class in the chain. Performance of such a case is significantly better: Before: ``` 1 thread Numeric.subclasses 1.315k (± 4.6%) i/s - 6.612k in 5.040800s 1 thread Object.subclasses 56.282 (± 3.6%) i/s - 285.000 in 5.074688s 1 thread Custom.subclasses with 1000 singletons 5.588 (± 0.0%) i/s - 28.000 in 5.021174s ``` After: ``` 1 thread Numeric.subclasses 2.402k (± 3.4%) i/s - 11.990k in 4.998757s 1 thread Object.subclasses 189.977 (± 6.8%) i/s - 954.000 in 5.068134s 1 thread Custom.subclasses with many singletons 3.439k (± 1.0%) i/s - 17.334k in 5.040639s ```
1 parent 422bb1c commit 0497910

File tree

1 file changed

+8
-5
lines changed

1 file changed

+8
-5
lines changed

core/src/main/java/org/jruby/RubyClass.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,7 +1106,7 @@ private int concreteSubclasses(RubyArray<RubyClass> subs) {
11061106
int clearedCount = 0;
11071107
while (subclassNode != null) {
11081108
RubyClass klass = subclassNode.ref.get();
1109-
subclassNode = subclassNode.next;
1109+
subclassNode = subclassNode.nextConcrete;
11101110

11111111
if (klass == null) {
11121112
clearedCount++;
@@ -1153,22 +1153,25 @@ private static SubclassNode rebuildSubclasses(SubclassNode subclassNode) {
11531153
RubyClass klass = ref.get();
11541154
subclassNode = subclassNode.next;
11551155
if (klass == null) continue;
1156-
newTop = new SubclassNode(ref, newTop);
1156+
newTop = new SubclassNode(klass, ref, newTop);
11571157
}
11581158
return newTop;
11591159
}
11601160

11611161
// TODO: make into a Record
11621162
static class SubclassNode {
11631163
final SubclassNode next;
1164+
final SubclassNode nextConcrete;
1165+
final boolean concrete;
11641166
final WeakReference<RubyClass> ref;
11651167
SubclassNode(RubyClass klass, SubclassNode next) {
1166-
ref = new WeakReference<>(klass);
1167-
this.next = next;
1168+
this(klass, new WeakReference<>(klass), next);
11681169
}
1169-
SubclassNode(WeakReference<RubyClass> ref, SubclassNode next) {
1170+
SubclassNode(RubyClass klass, WeakReference<RubyClass> ref, SubclassNode next) {
11701171
this.ref = ref;
11711172
this.next = next;
1173+
this.nextConcrete = next == null ? null : next.concrete ? next : next.nextConcrete;
1174+
this.concrete = !klass.isSingleton();
11721175
}
11731176
}
11741177

0 commit comments

Comments
 (0)