问题
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:
- Execute stuff
- Execute randomly different stuff (to prevent branch prediction if any)
- 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