Is it too costly to do a (Runnable & Serializable) when you want to send an anonymous function?

廉价感情. 提交于 2020-01-02 08:44:15

问题


I am doing sht like:

 executor.execute((Runnable & Serializable)func);

Where func is an anonymous function, I have to heavily use this in the project otherwise I would have to create a class for every different function I want to call and implement Runnable and Serializable in each of those class, the advantage would be that I would have the type at compile time rather than casting it at run time, I would like to know if doing this cast is too costly or is trivial and does not represent a big gap in performance.

If you have a real life experience on this and you are willing to share it, it would be awesome.

Thanks in advance.


回答1:


Casting is "almost trivial" in that it only verifies that the given object extends some class or implements an interface - it does not change the object itself.

In your case, if func stands for a lambda expression or a method reference

executor.execute((Runnable & Serializable) () -> System.out.println("5"));
executor.execute((Runnable & Serializable) System.out::println);

the LambdaMetafactory guarantees that the generated lambda object really implements Runnable and Serializable and the cast might even get optimized away.

If however func is a parameter to your method:

public void execute(Runnable func) {
    executor.execute((Runnable & Serializable)func);
}

neither the java compiler nor the java runtime will somehow magically make func Serializable too.

In this case, you could rewrite your method as

public <T extends Runnable & Serializable> void execute(T func) {
    executor.execute(func);
}

which requires the caller to provide a Runnable and Serializable object - either an autogenerated one (through lambda expression or method reference) or a "manually" coded class.




回答2:


In most cases, doing any operation at runtime ends up being more costly that its compile time alternative. There are cases when this isn't true but mostly the JVM does a better job. For most cases that I've seen such implementations, it has been more costly to do this. In the end it would really depend on the amount of tasks you will run. If they are a lot, I'd recommend using an interface or abstraction here. Something along the lines of ....

public interface RunnableSerializable extends Runnable, Serializable {
     // override and add as necessary
}

public class MyRunnableClass implementes RunnableSerializable {
    // your runnable code
}

MyRunnableClass clazz = ...
executor.execute(clazz);

If its just very few of runnables every few minutes (or more), type-casting should be fine.




回答3:


The cast itself is really trivial: it's just an if-else which would be optimized by JIT and branch predictor because you're passing valid classes all the time (you don't see any ClassCastException's).

I think the real question here if there is any difference between executing (by making a virtual invoke through an interface via Runnable.run()) an anonymous lambda or a declared class. So I've set up a JMH benchmark to test 3 following cases using lambdas and declared classes:

  1. Execute stuff
  2. Execute randomly different stuff (to prevent branch prediction if any)
  3. Execute randomly different stuff, but some stuff would occur more often (to allow branch prediction if any)

The result show lambdas are slower for a matter of nanoseconds, so it's really looks like no difference between labmda or declared class at all:

    Benchmark                                                                                Mode      Cnt         Score    Error  Units
    MyBenchmark.testAnonymousLambda                                                        sample  7272891        16.150 ±  1.646  ns/op
    MyBenchmark.testDeclared                                                               sample  7401499        15.349 ±  3.648  ns/op
    MyBenchmark.testRandomAnonymousLambda                                                  sample  6851255      3314.151 ± 11.655  ns/op
    MyBenchmark.testRandomBranchingDeclared                                                sample  6887926      3293.818 ±  9.180  ns/op
    MyBenchmark.testPredictableAnonymousLambda                                             sample  3990711      6091.766 ± 25.912  ns/op
    MyBenchmark.testPredictableBranchingDeclared                                           sample  3993885      6055.421 ± 21.535  ns/op

So in answer to your question it doesn't really matter if you would cast, or if you would use a lambda instead of creating a set of declared classes.

P.S. the benchmark code is available via a gist



来源:https://stackoverflow.com/questions/39669094/is-it-too-costly-to-do-a-runnable-serializable-when-you-want-to-send-an-anon

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!