-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Optimize find ancestor #4294
Optimize find ancestor #4294
Conversation
Thank you for this suggestion. Before accepting, could you share your test application? |
sure, here we go! generally streams should be used with care as they are not only slower than the traditional alternatives, but also create temporary objects which cannot be optimized away by the JIT until now (including Java 21).
|
You highlight the result of a micro-benchmark which shows an overload linked to the use of streams. But I don't think that eliminating the use of streams in this case will significantly improve the way JP works. Furthermore, although the results seem correct, your test case isn't because in the case of the "current" implementation the JP logic is tested twice, and in the case of the "improved" implementation the JP logic is systematically executed. We would like to thank you for your investigative work, but we are not going to accept your proposal until we can demonstrate a significant improvement in JP's performance. In the case you highlight, the use of streams makes the processing a little more readable. However, I'll leave this PR open if you'd like to add to your demonstration or correct your test case. |
First of all I have to say that I am somehow embarassed that the benchmark I sent to you is not fully correct, sorry for that. You can find the corrected benchmark appended together with the updated performance figures. As I did not fully understand all your comments, I also added testCurrentJavaparser to prove that findAncestorCurrent and the implementation actually in the library behave identical. After the correction, the performance and allocation load are now improved even more compared to the current implementation, now by factor 4. Of course these numbers depend on the code snippet used etc, but it nevertheless shows that an improvement is possible by changing a single line of code.
|
Your test case is still not correct. It seems to me that this is what you want to test on the current implementation.
We are ready to improve the operation of JP as soon as it makes sense and the improvement is clearly perceptible. In the case you present, the pressure on memory is marginal (gc.count and gc.time), and the memory allocation rate is not a determining factor in the choice of optimisation either. All that's left is the rate of operations per second, which is much higher in the version you're proposing than in the current implementation. This is probably due to the use of streams. But once again the micro-benchmark raises a problem of consistency because this method is rarely used in loops. Furthermore, when used in a loop, the jit compiler improves the efficiency of the method from the second iteration onwards. My feeling is that this improvement is very marginal and will not bring any visible results to JP users. If you want to improve JP, I suggest you use a profiler on a concrete case and identify hotspots (memory, cpu, etc.). We can then see how to improve JP's behaviour in these use cases. |
IMHO the test case is correct now: testCurrentJavaparser() tests the current implementation contained in the JP library, testCurrent() tests the same code but copied into the test code as findAncestorCurrent() so it can easily be compared with findAncestorImproved(). As said, it will be hard to come up with a more meaningful benchmark. Finally it's up to you to make the decision, so feel free to close the PR. |
Thank you for your proposal and the time you've devoted to it, but as I've already told you, given that the improvement is marginal, I prefer to focus on visibility rather than the performance of an algorithm. |
Finally, I'm reconsidering my initial position, even if the improvement is marginal. In fact, the current code is no more readable than the one in your proposal, so we can accept this one. Thank you for your contribution. |
that was now really an unexpected change after the discussion, but thanks for accepting. |
I simply reread the current code and it didn't seem any easier to read than your proposal. I'm just waiting to be convinced by the new proposals. Yours was just on the edge. Thank you for your insistence, which has enabled me to challenge my position. |
HasParentNode.findAncestor() uses Arrays.stream() to handle the varargs types parameters.
This may be convenient, but is both inefficient in terms of performance and memory consumption.
The proposed new implementation uses the good old iteration which improves numbers by factor 2, see JMH benchmark below.