1

Recently we decided to migrate to Java 8 from Java 7, hence we made some researches and performance tests. We read that there are no objects created for the lambda expressions. ("it seems that it's true") In some articles there is also mentioned that instantiation of lambda expressions can be 100x faster then abstract classes while there is no class loading and instantiation steps, but there are still linkage and capture steps. By the way it's not 100% clear for us what happens in the linkage and capture steps.

By the way we made a comparison between a simple abstract class and lambda expression.

The question for us was whether we can or we should not write lambda expressions in loops.

Abstract class:

Comparator<String> c;

String s1 = "a", s2 = "b";

long time = System.currentTimeMillis();
for (long i = 0; i < it; i++) {
  c = new Comparator<String>() {
    /**
     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
     */
    public int compare(String o1, String o2) {
      return o1.compareTo(o2);
    }
  };

  c.compare(s1, s2);
}

System.out.println("Anonymous class: " + (System.currentTimeMillis() - time));

Lambda:

Comparator<String> c;

String s1 = "a", s2 = "b";

long time = System.currentTimeMillis();
for (long i = 0; i < it; i++) {
  c = (o1, o2) -> o1.compareTo(o2);

  c.compare(s1, s2);
}

System.out.println("Anonymous class: " + (System.currentTimeMillis() - time));

In performance perspective there are a lot of classes created for the anonymous classes but there is no memory overhead in case of the lambda expressions.That's fine, but the problem starts with the performance: Anonymous class test: 607ms Lambda test: 1182ms => ~94% slower

It's interesting that the lambda expression variant is much slower than the anonymous one.

Note: just processing the c.compare code takes at around 600ms, which indicates for us that there is no performance overhead for creating a lot of instances of anonymous classes inside the for loop. But the instances are created, it clearly visible if we are looking to the heap of the application when we are processing the anonymous test.

Could somebody explain to us why the lambda version is much slower than the anonymous test version? As we understood the lambda expressions are compiled to static methods inside the original class, but there are also that capture and linkage steps which are not totally clear for us.

Micha Wiedenmann
  • 19,979
  • 21
  • 92
  • 137
  • Lambda takes a lot longer to start esp the very first time as it has to generate the class at runtime. – Peter Lawrey Jun 29 '18 at 10:03
  • [How do I write a correct micro-benchmark in Java?](https://stackoverflow.com/q/504103/2711488) • [Java lambdas 20 times slower than anonymous classes?](https://stackoverflow.com/a/34642063/2711488) – Holger Jun 29 '18 at 10:05
  • By the way, your method of benchmarking is **extremely** oversimplistic. Please do no rely upon any such results to inform your future decisions. See https://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java – Michael Jun 29 '18 at 10:06
  • @Holger if I understand correctly, the benchmark is affected by the the lambda framework being loaded when encountering the first lambda? So creating another lambda before starting the timer could change the outcome? – Malte Hartwig Jun 29 '18 at 10:09
  • @MalteHartwig exactly. As said in the linked answer, you can also verify by raising the number of lambda expressions, to see, that the additional lambdas make no difference. – Holger Jun 29 '18 at 10:12
  • Just tested it by putting `c = (o1, o2) -> o1.compareTo(o2);` in front of the loop, and indeed, the difference is reduced to about 0. – Malte Hartwig Jun 29 '18 at 10:12
  • Just tested it by putting c = (o1, o2) -> o1.compareTo(o2); ... Yes, but we are testing the the difference between an anonymous class and the lambda if they are in a loop. – Arnold Robert Turdean Jun 29 '18 at 10:35
  • What I meant was that I **added** it, not **moved**, the lambdas are also in the loop. The point is that in this small test, the first lambda that the VM encounters is inside your timer. This caused the necessary lambda parsing classes to be loaded in the first loop execution while your timer was already running which is why the time is so high. In a real-world application, this is probably not going to matter much: You start the VM, some code executes the first lambda and has to load the lambda framework, but all other millions of lambdas afterwards are just as fast as anonymous classes. – Malte Hartwig Jun 29 '18 at 10:43
  • @MalteHartwig For me the same time remained even if I added the mentioned code before the loop and also before the timing (time = System.currentTimeMillis()) , could you link the code you made ? – Arnold Robert Turdean Jun 29 '18 at 11:07
  • Note: the "it" (iteration number) is equal with Integer.MAX_VALUE. – Arnold Robert Turdean Jun 29 '18 at 11:09
  • @ArnoldRobertTurdean Compare with [this run on tio.run](https://tio.run/##zVNNT8MwDD23v8LqKWNbWXdkH9KEdkDaThsICSGUtWHLaJIqcfehab99JGkHEggOICEOlR2/Z8d@Ttd0Q9vr7OXERaE0wtqe4xJ5Hl/0wrAoFzlPIc2pMTClXMLhFNRBgxSt2SiegXAQmaHmcvnwCFQvTQNwpdXWVBVzKpfxeJeyArmSYXAIg0AwXKksITcS2ZLpeDq6f7obTW7Hjd4b2v0W/fPcYxjUY3OJcC7kfI4NcEM5PyuF2MMAOq7etRIF1RSV7lf6DCF18eoAJrHEiEYtMF3nLSIH5spCyAWzodneIBOxpFLNbYT4Lp@VBuJZ3F9kTd/2YG2zWTUCkFpEsi187oCcKQD1Ml3bqeexeo2gkhac3e57AoBmWGpp8bjOmCtiGb2acPT2WB0rKZoDSM9kYhI3q6c7aj2eKjEu7GWYSxKNpJJ7oUpTvbwriKBpn9dHIaDtNWpcJk@dTsd9vmrdn7/6i511//POiFPeSd4efiHyz1SdULHIKLBdoZkx9jf8ra6n4@kV)… – Holger Jun 29 '18 at 19:21

0 Answers0