Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JRuby+Truffle: Keyword Arguments Optimization #2588

Merged
merged 10 commits into from Feb 13, 2015

Conversation

fniephaus
Copy link
Contributor

Hi @chrisseaton ,
this PR speeds up keyword arguments in JRuby+Truffle as discussed earlier (#2567).

  • Argument nodes are moved from RubyCallNode to DispatchNode
  • Keyword arguments are now being treated as normal named arguments
  • The method argument node array is specific for receiver types
  • In the arguments array, we use MarkerNode and MarkerNode.Marker to indicate that a method call with keyword arguments is optimized, i.e., the HashLiteralNode is expanded (removed and values extracted into the array)

Feedback and thorough review would be appreciated (and necessary).

Regards,
@matthias-springer, @jgraichen, @mswart and @fniephaus

More details can be found here:

@chrisseaton
Copy link
Contributor

Thanks very much for this. I'll go through it today. @nirvdrum and @eregon can you also take a look? I would say straight away that you still have one failing language spec that needs to be looked at - it doesn't look too complicated though.

@fniephaus
Copy link
Contributor Author

We're working on it! Thanks for taking a look.

@nirvdrum
Copy link
Contributor

This is great news. One general comment though, please replace all tab characters with 4 spaces. We should try to keep the formatting consistent with the rest of the code.

@fniephaus
Copy link
Contributor Author

Thanks for the feedback, @nirvdrum. We'll fix the indent asap.
@matthias-springer and I will also work on the failing language spec tonight. We already know how to fix it.

@@ -34,21 +29,12 @@

@Child private RubyNode receiver;
@Child private ProcOrNullNode block;
@Children private final RubyNode[] arguments;
private final RubyNode[] arguments;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why removing the @Children annotation here?

@eregon
Copy link
Member

eregon commented Feb 12, 2015

Looks good, I had a quick look through the code.
What was the decision behind using a MarkerNode, compared to other alternatives?

@eregon eregon assigned chrisseaton and eregon and unassigned chrisseaton and eregon Feb 12, 2015
@matthias-springer
Copy link
Contributor

We use MarkerNode to indicate that a call site is optimized, i.e., the Hash is expanded and keyword arguments can be read directly from the arguments array instead of the last argument which is a Hash. Now that I though about it again, I think it may be better to add another (boolean) flag to the beginning of the arguments array.

We removed the @Children annotation because we moved the argument nodes to the dispatch node. We should remove the filed from RubyCallNode entirely.

return new Marker();
}

public static class Marker {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the pros/cons of using the singleton pattern here?

@eregon
Copy link
Member

eregon commented Feb 12, 2015

I see, having the (second) marker at the end seems natural as it would be where the Hash is in the non-optimized case. I don't know what is best in case of rest arguments.

…kwargs

Conflicts:
	truffle/src/main/java/org/jruby/truffle/nodes/dispatch/CachedBoxedMethodMissingDispatchNode.java
	truffle/src/main/java/org/jruby/truffle/nodes/dispatch/UncachedDispatchNode.java
@fniephaus
Copy link
Contributor Author

@chrisseaton All tests are passing now. Feel free to give more feedback

}

@ExplodeLoop
protected Object executeArguments(VirtualFrame frame, Object argumentOverride) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering whether there's a better way to implement this. Argument nodes for calls are now stored in DispatchNode, but there are still places where argument objects (no nodes, but objects) are passed when calling the dispatch head node (e.g. ArrayNodes.inject). That's why we are keeping this option (not storing the argument nodes in DispatchNode but passing objects as before) still open.

@chrisseaton
Copy link
Contributor

Still a few tabs, but I'll merge it now so we can get on and run our CI benchmarks. Can you go back and fix the tabs and submit a new PR for that?

chrisseaton added a commit that referenced this pull request Feb 13, 2015
JRuby+Truffle: Keyword Arguments Optimization
@chrisseaton chrisseaton merged commit 5dc368f into jruby:master Feb 13, 2015
@fniephaus
Copy link
Contributor Author

@chrisseaton sure, will do. Thanks!

chrisseaton added a commit that referenced this pull request Feb 15, 2015
This reverts commit 5dc368f, reversing
changes made to 08da5c5.
@chrisseaton
Copy link
Contributor

I'm afraid I had to revert these merges temporarily. We've got some regressions on our benchmarks after pulling your branch in, and we don't want to leave any regressions in if we can't fix them in a day as it may mask other regressions.

I've made a branch truffle-kwargs with your changes on them, and we'll try to fix the regressions there. Hopefully it's something minor as it looked like your work shouldn't impact if keyword arguments aren't being used. I'd take an initial look and will let you know what the problem might be, but this might not be until Tuesday.

@fniephaus
Copy link
Contributor Author

Hey @chrisseaton,
Do you have any news on this?

@chrisseaton
Copy link
Contributor

I'm going to wait a few days, as we are changing how the PE works in Truffle and probably doing a new release of Graal. It makes sense to wait for those before exploring the problem here.

I don't anticipate it taking long to fix. Do you need it to be merged into master as part of the requirements of your project?

@fniephaus
Copy link
Contributor Author

No, I don't think so. The code is already in the repository which should be enough I guess.
I didn't know about the new release of Graal, so I thought it might be better to fix it asap in order to keep the effort as low as possible.

@chrisseaton
Copy link
Contributor

Yeah we won't leave it more than a week max.

@fniephaus
Copy link
Contributor Author

No worries. Let us know if we can be of any help. Otherwise, we will leave this with you if that's ok :)

@chrisseaton
Copy link
Contributor

Did you have any benchmarks for testing that keyword arguments were properly optimised?

@fniephaus
Copy link
Contributor Author

Yes, sure we did, but they were very simple.

Here's one:

def m(foo:, bar:)
  foo + bar
end

200.times.each do |times|
    beginning_time = Time.now
    (1..1000000).each do |i|
        m(foo: 1, bar: 2)
    end
    end_time = Time.now
    puts "Time elapsed #{(end_time - beginning_time)*1000} milliseconds"
end

and here's a more complex one:

def m(foo:, bar:)
  foo + bar
end

def n(foo = 1, bar = 2)
  foo + bar
end

def o(foo, bar:)
    foo + bar
end

def p(a, b=2, c:)
    a + b + c
end

def q(foo:1, bar:2)
    foo + bar
end

def r(a, b=2, c=3, d:, e:5, f:)
    a+b+c+d+e+f
end

def s(a, b=2, c:, d:, **rest)
    a+b+c+d+rest[:e]
    #rest[:e]
end

def t(**r)
    r[:a]+r[:b]
end

def u(a, b)
    a + b[:a] + b[:b]
end

def assert(a, b)
if a != b
    puts "error: " + a.to_s + "!=" + b.to_s
end
end

200.times.each do |times|
    beginning_time = Time.now
    (1..1000000).each do |i|
        assert(n(1, 2), 3)
        assert(m(foo: 1, bar: 2), 3)
        assert(m({foo: 1, bar:2}), 3)
        assert(o(1, bar: 2), 3)
        assert(o(1, {bar:2}), 3)
        assert(p(1, c:3), 6)
        assert(q(foo:1, bar:2), 3)
        assert(q(foo:1), 3)
        #assert(q(), 3)
        assert(r(1,f:6,d:4), 21)
        assert(s(1,e:5, f:6,d:4,c:3), 15)
        assert(t(a:1, b:2, c:3), 3)
        assert(u(1, {a: 2, b: 3}), 6)
    end
    end_time = Time.now
    puts "Time elapsed #{(end_time - beginning_time)*1000} milliseconds"
end

@chrisseaton
Copy link
Contributor

Maybe I'll just convert some of the existing benchmarks to use kwargs.

@fniephaus
Copy link
Contributor Author

Haha, you probably should!

@chrisseaton chrisseaton modified the milestone: truffle-dev May 4, 2015
@enebo enebo added this to the Non-Release milestone Dec 7, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants