Performance of lambda expressions in Java 8
In mid-2013, with the release of Java 8, the language began to support lambda expressions, since then 4 years have passed, a lot of updates have been released, and Java 9 is coming soon (which we can probably see this year, despite constant postponements), so at the junction of time, I would like to summarize and evaluate the performance of a new tool in java and an old tool like the world, giving it a quantitative assessment.
Performance evaluations have already been made several times, as other things discussed the advantages and disadvantages of lambda expressions:
the first include:
- simplification and reduction of code
- the possibility of implementing a functional style
- the possibility of parallel computing
to the second :
- decreased performance
- debug's difficulty. A good
technique and tests were described and used 5 years ago by the user dmmm ( https://habrahabr.ru/post/146718/ ), and they were taken as a basis.
If, in short, the tests themselves were supplied with the LambdaJ project . dmmm added tests for the Guava project and adjusted them after discussion with the habr community (2012). By
me (that is blutovi) all lambda tests were rewritten (since testing took place on alpha versions of java, and in the current state the code simply cannot be compiled), the memory status was also analyzed using Java Mission Control and a jfr file with the results was attached for each test. (Java Mission Control, a JVM diagnostic and monitoring tool, has been available since java 7 update 40, as a result of combining the ideas of two JVMs: HotSpot from Sun and JRockit from Oracle).
The junit tests themselves consist of three phases: initialization of collections, idle execution, execution with statistics collection (time counting is carried out only at the last stage, and analysis of working with memory is carried out throughout the cycle).
Time measurement is carried out as follows: for each of iterations over time , a sequential call of the test algorithm occurs. Upon its expiration, the arithmetic mean time of the algorithm execution is determined:
by dividing the total execution time by the number of times which he managed to fulfill. Based iterations, we find the total average execution time of the algorithm:
By magnitude (number of iterations) and (time during which the test algorithm is called) can be influenced by changing the values of the constants in the class ch.lambdaj.demo.AbstractMeasurementTest:
The tests were run with the activated parameters for the operation of Java Mission Control, which allowed the automatic generation of diagnostic files.
in absolute values (values than smaller , the better )
in relative values (100% - this base than the value smaller , the better )
To summarize, it can be noted that the same things can be implemented using lambda expressions in different ways: however, even the most optimal options for performance will be inferior to simple solutions, and not the optimal ones can significantly affect performance.
Thus, the iterative approach makes it possible to focus on execution speed , while Guava allows you to focus on the amount of memory used , minimizing it. Using lambda expressions seems to give only aesthetic pleasure (to my great surprise).
Any suggestions on how to change and improve tests are welcome.
Performance evaluations have already been made several times, as other things discussed the advantages and disadvantages of lambda expressions:
the first include:
- simplification and reduction of code
- the possibility of implementing a functional style
- the possibility of parallel computing
to the second :
- decreased performance
- debug's difficulty. A good
technique and tests were described and used 5 years ago by the user dmmm ( https://habrahabr.ru/post/146718/ ), and they were taken as a basis.
If, in short, the tests themselves were supplied with the LambdaJ project . dmmm added tests for the Guava project and adjusted them after discussion with the habr community (2012). By
me (that is blutovi) all lambda tests were rewritten (since testing took place on alpha versions of java, and in the current state the code simply cannot be compiled), the memory status was also analyzed using Java Mission Control and a jfr file with the results was attached for each test. (Java Mission Control, a JVM diagnostic and monitoring tool, has been available since java 7 update 40, as a result of combining the ideas of two JVMs: HotSpot from Sun and JRockit from Oracle).
How was it tested?
The junit tests themselves consist of three phases: initialization of collections, idle execution, execution with statistics collection (time counting is carried out only at the last stage, and analysis of working with memory is carried out throughout the cycle).
Time measurement is carried out as follows: for each of iterations over time , a sequential call of the test algorithm occurs. Upon its expiration, the arithmetic mean time of the algorithm execution is determined:
by dividing the total execution time by the number of times which he managed to fulfill. Based iterations, we find the total average execution time of the algorithm:
By magnitude (number of iterations) and (time during which the test algorithm is called) can be influenced by changing the values of the constants in the class ch.lambdaj.demo.AbstractMeasurementTest:
public static final int WARMUP_ITERATIONS_COUNT = 10;
public static final int ITERATIONS_COUNT = 10;
public static final int ITERATION_DURATION_MSEC = 1000;
The tests were run with the activated parameters for the operation of Java Mission Control, which allowed the automatic generation of diagnostic files.
results
in absolute values (values than smaller , the better )
for | iterate | guava | jdk8 lambda | |||||
---|---|---|---|---|---|---|---|---|
time, ns | heap Mb | time, ns | heap Mb | time, ns | heap Mb | time, ns | heap Mb | |
Extract cars original cost | 220.4 | 313 | 200.6 | 324 | 188.1 | 302 | 266.2 | 333 |
Age of youngest who bought for> 50k | 6367 | 260 | 4245 | 271 | 6367 | 260 | 6157 | 278 |
Find buys of youngest person | 6036 | 270 | 6411.4 | 259 | 6206 | 260 | 6235 | 288 |
Find most bought car | 2356 | 167 | 2423 | 171 | 5882 | 193 | 2971 | 190 |
Find most costly sale | 49 | 71 | 58.1 | 66 | 431.6 | 297 | 196.1 | 82 |
Group sales by buyers and sellers | 12144 | 250 | 12053 | 254 | 8259 | 206 | 18447 | 242 |
Index cars by brand | 263.3 | 289 | 275.0 | 297 | 2828 | 226 | 307.5 | 278 |
Print all brands | 473.2 | 355 | 455.3 | 365 | 540.3 | 281 | 514.2 | 337 |
Select all sales of a Ferrari | 199.3 | 66 | 265.1 | 53 | 210.4 | 111 | 200.2 | 65 |
Sort sales by cost | 1075 | 74 | 1075 | 74 | 1069 | 72 | 1566 | 124 |
Sum costs where both are males | 67.0 | 63 | 72.9 | 58 | 215.9 | 88 | 413.7 | 114 |
for | iterate | guava | jdk8 lambda | |||||
---|---|---|---|---|---|---|---|---|
time | heap | time | heap | time | heap | time | heap | |
Extract cars original cost | 117% | 104% | 107% | 107% | 100% | 100% | 142% | 110% |
Age of youngest who bought for> 50k | 150% | 100% | 100% | 104% | 150% | 100% | 145% | 107% |
Find buys of youngest person | 100% | 104% | 106% | 100% | 103% | 100% | 103% | 111% |
Find most bought car | 100% | 100% | 103% | 102% | 250% | 116% | 126% | 114% |
Find most costly sale | 100% | 108% | 119% | 100% | 881% | 450% | 400% | 124% |
Group sales by buyers and sellers | 147% | 121% | 146% | 123% | 100% | 100% | 223% | 117% |
Index cars by brand | 100% | 128% | 104% | 131% | 1074% | 100% | 117% | 123% |
Print all brands | 104% | 126% | 100% | 130% | 119% | 100% | 113% | 120% |
Select all sales of a Ferrari | 100% | 125% | 133% | 100% | 106% | 209% | 100% | 123% |
Sort sales by cost | 101% | 103% | 101% | 103% | 100% | 100% | 147% | 172% |
Sum costs where both are males | 100% | 109% | 109% | 100% | 322% | 152% | 617% | 197% |
conclusions
To summarize, it can be noted that the same things can be implemented using lambda expressions in different ways: however, even the most optimal options for performance will be inferior to simple solutions, and not the optimal ones can significantly affect performance.
Thus, the iterative approach makes it possible to focus on execution speed , while Guava allows you to focus on the amount of memory used , minimizing it. Using lambda expressions seems to give only aesthetic pleasure (to my great surprise).
Any suggestions on how to change and improve tests are welcome.
References
- Source code and test results are available in the git repository.
- Diagnostic files in one archive
- Download Java 8 (at the time of testing, latest version 8u131)