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

Return values from Java are converted to Ruby even if you're not using them #1407

Closed
hakanai opened this issue Jan 15, 2014 · 2 comments
Closed

Comments

@hakanai
Copy link

hakanai commented Jan 15, 2014

Something I found quite by accident while benchmarking a method of ours. We had a test along these lines:

method = :whatever
10_000_000.times do
  obj.send(method)
end

I was profiling to see where it was spending its time and over 90% of the time, it was here:

"SwingWorker-pool-14-thread-3@10177" daemon prio=5 tid=0x72 nid=NA runnable
java.lang.Thread.State: RUNNABLE
      at java.lang.System.identityHashCode(System.java:-1)
      at org.jruby.javasupport.util.ObjectProxyCache.hash(ObjectProxyCache.java:174)
      at org.jruby.javasupport.util.ObjectProxyCache.getOrCreate(ObjectProxyCache.java:163)
      at org.jruby.javasupport.Java.getInstance(Java.java:392)
      at org.jruby.javasupport.Java.getInstance(Java.java:371)
      at org.jruby.javasupport.JavaUtil.convertJavaToUsableRubyObject(JavaUtil.java:167)
      at org.jruby.javasupport.JavaMethod.convertReturn(JavaMethod.java:517)
      at org.jruby.javasupport.JavaMethod.invokeDirectWithExceptionHandling(JavaMethod.java:441)
      at org.jruby.javasupport.JavaMethod.invokeDirect(JavaMethod.java:304)
      at org.jruby.java.invokers.InstanceMethodInvoker.call(InstanceMethodInvoker.java:52)
      at org.jruby.java.invokers.InstanceMethodInvoker.call(InstanceMethodInvoker.java:113)
      at org.jruby.internal.runtime.methods.AliasMethod.call(AliasMethod.java:81)
      at org.jruby.RubyClass.finvoke(RubyClass.java:527)
      at org.jruby.RubyBasicObject.send19(RubyBasicObject.java:1504)
      at org.jruby.RubyKernel.send19(RubyKernel.java:2240)
      at org.jruby.RubyKernel$INVOKER$s$send19.call(RubyKernel$INVOKER$s$send19.gen:-1)
      at org.jruby.internal.runtime.methods.JavaMethod$JavaMethodOneOrNBlock.call(JavaMethod.java:350)
      at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:168)
      at org.jruby.ast.CallOneArgNode.interpret(CallOneArgNode.java:57)

It seems like JRuby is spending over 90% of the time preparing the return value so that the Ruby script can use it, even though the script doesn't use it.

@chrisseaton
Copy link
Contributor

I appreciate the problem, but this is probably not going to be easy to fix.

The problem is that the value is produced in one place, converted in another, and then used (or not) somewhere else. To elide the conversion based on a missing use, we have to inform the conversion site.

One way to do this would be to introduce lazy conversion and shift the conversion site so it's in the same place as the use - we return an object that knows how to convert to Ruby if it ever needs to do so. But that introduces an extra layer of indirection, and so will probably significantly harm overall performance.

The other way to do this would be to use some data flow analysis to determine that a value is never used and does not need to be converted. We would then also need to inline the conversion so that it can be specialised based on that analysis. Unfortunately this just isn't something that JRuby is designed to do at the moment. It would be nice if the JVM could do this automatically from our generated byte code, but again that just isn't going to happen at the moment.

Your problem is probably compounded here by using #send, which makes any indirection or optimisation even less likely in current JRuby.

I would suggest a workaround of writing a wrapper method in Java that returns null.

@enebo or @headius might want to considering marking this as a nice idea but not going to happen any time soon (won't fix), or they might have some ideas of their own.

@kares kares added this to the Won't Fix milestone Aug 23, 2015
headius added a commit that referenced this issue Sep 12, 2016
I botched the previous patch a bit by unconditionally re-assigning
the secureRandom local to a default JDK new SecureRandom. This
could cause systems without the default preferred PRNG
(NativePRNGNonBlocking, Java 8+) to have slower thread startup and/or
random number generation.
headius added a commit to headius/jruby that referenced this issue Sep 12, 2016
I botched the previous patch a bit by unconditionally re-assigning
the secureRandom local to a default JDK new SecureRandom. This
could cause systems without the default preferred PRNG
(NativePRNGNonBlocking, Java 8+) to have slower thread startup and/or
random number generation.
@enebo
Copy link
Member

enebo commented Feb 17, 2017

We do detect unused values in our IR but our IR knows nothing of Java Integration features so our ability to detect this now is not there.

@enebo enebo closed this as completed Feb 17, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants