Here's the latest iteration of my patch to pre-allocate and re-use Frame
objects. It doesn't yet pass all tests, but it appears to help performance:
without:
Test interpreted: 100k loops calling self's foo 100 times
2.523000 0.000000 2.523000 ( 2.524000)
1.902000 0.000000 1.902000 ( 1.902000)
2.043000 0.000000 2.043000 ( 2.043000)
1.974000 0.000000 1.974000 ( 1.974000)
2.163000 0.000000 2.163000 ( 2.163000)
2.203000 0.000000 2.203000 ( 2.203000)
2.220000 0.000000 2.220000 ( 2.220000)
2.217000 0.000000 2.217000 ( 2.217000)
2.164000 0.000000 2.164000 ( 2.164000)
2.284000 0.000000 2.284000 ( 2.284000)
with:
Test interpreted: 100k loops calling self's foo 100 times
2.065000 0.000000 2.065000 ( 2.064000)
1.692000 0.000000 1.692000 ( 1.692000)
1.914000 0.000000 1.914000 ( 1.914000)
1.884000 0.000000 1.884000 ( 1.885000)
1.879000 0.000000 1.879000 ( 1.879000)
1.877000 0.000000 1.877000 ( 1.877000)
1.872000 0.000000 1.872000 ( 1.873000)
1.872000 0.000000 1.872000 ( 1.872000)
1.870000 0.000000 1.870000 ( 1.870000)
1.906000 0.000000 1.906000 ( 1.906000)
It does pass a large number of tests; I think there's only a bit more
work needed to get it passing everything, and it seems like it could be
a reasonable win.
- Charlie
Index: src/org/jruby/runtime/ThreadContext.java
===================================================================
--- src/org/jruby/runtime/ThreadContext.java (revision 3914)
+++ src/org/jruby/runtime/ThreadContext.java (working copy)
@@ -122,6 +122,10 @@
// TOPLEVEL self and a few others want a top-level scope. We create
this one right
// away and then pass it into top-level parse so it ends up being the
top level.
pushScope(new DynamicScope(new LocalStaticScope(null), null));
+
+ for (int i = 0; i < frameStack.length; i++) {
+ frameStack[i] = new Frame();
+ }
}
CallType lastCallType;
@@ -193,6 +197,10 @@
System.arraycopy(frameStack, 0, newFrameStack, 0,
frameStack.length);
+ for (int i = frameStack.length; i < newSize; i++) {
+ newFrameStack[i] = new Frame();
+ }
+
frameStack = newFrameStack;
}
}
@@ -288,26 +296,48 @@
//////////////////// FRAME MANAGEMENT ////////////////////////
private void pushFrameCopy() {
- pushFrame(getCurrentFrame().duplicate());
+ Frame currentFrame = getCurrentFrame();
+ Frame frame = frameStack[++frameIndex];
+ if (frame.isShared()) {
+ frame = frameStack[frameIndex] = new Frame();
+ }
+ frame.updateFrame(currentFrame);
+ expandFramesIfNecessary();
}
+ private void pushFrame(Frame frame) {
+ frameStack[++frameIndex] = frame;
+ expandFramesIfNecessary();
+ }
+
private void pushCallFrame(RubyModule clazz, String name,
IRubyObject self, IRubyObject[] args, int req,
Block block, Object jumpTarget) {
- pushFrame(new Frame(clazz, self, name, args, req, block,
getPosition(), jumpTarget));
+ pushFrame(clazz, name, self, args, req, block, jumpTarget);
}
-
- private void pushFrame() {
- pushFrame(new Frame(getPosition()));
+
+ private void pushFrame(RubyModule clazz, String name,
+ IRubyObject self, IRubyObject[] args, int req,
Block block, Object jumpTarget) {
+ Frame frame = frameStack[++frameIndex];
+ if (frame.isShared()) {
+ frame = frameStack[frameIndex] = new Frame();
+ }
+ frame.updateFrame(clazz, self, name, args, req, block, getPosition(),
jumpTarget);
+ expandFramesIfNecessary();
}
- private void pushFrame(Frame frame) {
- frameStack[++frameIndex] = frame;
+ private void pushFrame() {
+ Frame frame = frameStack[++frameIndex];
+ if (frame.isShared()) {
+ frame = frameStack[frameIndex] = new Frame();
+ }
+ frame.updateFrame(getPosition());
expandFramesIfNecessary();
}
private void popFrame() {
Frame frame = (Frame)frameStack[frameIndex];
- frameStack[frameIndex--] = null;
+ //frameStack[frameIndex--] = null;
+ frameIndex--;
setPosition(frame.getPosition());
}
Index: src/org/jruby/runtime/Block.java
===================================================================
--- src/org/jruby/runtime/Block.java (revision 3914)
+++ src/org/jruby/runtime/Block.java (working copy)
@@ -126,6 +126,9 @@
this.iterNode = iterNode;
this.self = self;
this.frame = frame;
+ if (this.frame != null) {
+ this.frame.setShared(true);
+ }
this.visibility = visibility;
this.klass = klass;
this.cref = cref;
Index: src/org/jruby/runtime/Frame.java
===================================================================
--- src/org/jruby/runtime/Frame.java (revision 3914)
+++ src/org/jruby/runtime/Frame.java (working copy)
@@ -80,7 +80,7 @@
* and also for block_given?. Both of those methods needs access to the
block of the
* previous frame to work.
*/
- private final Block block;
+ private Block block;
/**
* Does this delimit a frame where an eval with binding occurred. Used
for stack traces.
@@ -93,6 +93,16 @@
private Visibility visibility = Visibility.PUBLIC;
private Object jumpTarget;
+
+ private boolean shared = false;
+
+ public boolean isShared() {
+ return shared;
+ }
+
+ public void setShared(boolean shared) {
+ this.shared = shared;
+ }
public Object getJumpTarget() {
return jumpTarget;
@@ -101,17 +111,35 @@
public void setJumpTarget(Object jumpTarget) {
this.jumpTarget = jumpTarget;
}
-
/**
* The location in source where this block/method invocation is happening
*/
- private final ISourcePosition position;
+ private ISourcePosition position;
- public Frame(ISourcePosition position) {
- this(null, null, null, IRubyObject.NULL_ARRAY, 0, Block.NULL_BLOCK,
position, null);
+ public void updateFrame(ISourcePosition position) {
+ updateFrame(null, null, null, IRubyObject.NULL_ARRAY, 0,
Block.NULL_BLOCK, position, null);
}
- public Frame(RubyModule klazz, IRubyObject self, String name,
+ public void updateFrame(Frame frame) {
+ assert frame.block != null : "Block uses null object pattern. It
should NEVER be null";
+
+ if (frame.args.length != 0) {
+ args = new IRubyObject[frame.args.length];
+ System.arraycopy(frame.args, 0, args, 0, frame.args.length);
+ } else {
+ args = frame.args;
+ }
+
+ this.self = frame.self;
+ this.requiredArgCount = frame.requiredArgCount;
+ this.name = frame.name;
+ this.klazz = frame.klazz;
+ this.position = frame.position;
+ this.block = frame.block;
+ this.jumpTarget = frame.jumpTarget;
+ }
+
+ public void updateFrame(RubyModule klazz, IRubyObject self, String name,
IRubyObject[] args, int requiredArgCount, Block block,
ISourcePosition position, Object jumpTarget) {
assert block != null : "Block uses null object pattern. It should
NEVER be null";
@@ -124,6 +152,14 @@
this.block = block;
this.jumpTarget = jumpTarget;
}
+
+ public Frame duplicate() {
+ Frame newFrame = new Frame();
+
+ newFrame.updateFrame(this);
+
+ return newFrame;
+ }
/** Getter for property args.
* @return Value of property args.
@@ -250,18 +286,6 @@
return block;
}
- public Frame duplicate() {
- IRubyObject[] newArgs;
- if (args.length != 0) {
- newArgs = new IRubyObject[args.length];
- System.arraycopy(args, 0, newArgs, 0, args.length);
- } else {
- newArgs = args;
- }
-
- return new Frame(klazz, self, name, newArgs, requiredArgCount, block,
position, jumpTarget);
- }
-
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
---------------------------------------------------------------------
To unsubscribe from this list please visit:
http://xircles.codehaus.org/manage_email