Enhance coverage collection performance by caching probe arrays local… #545
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR significantly improves and simplifies the performance of coverage collection. This PR is related to #534, which also modifies coverage collection (sorry for the delay in putting this together, a few other things came up in the meantime).
The prior mechanism for collecting coverage involved hitting a HashMap at least once in each method execution to store coverage probes (there were several different ways that this might happen depending on the size of a method). This PR simplifies coverage collection so that there only is a single access to this shared HashMap (when a class is initialized). A static field is added to each class with a probe array, within each method this static field is cached in a local variable, and then when any probe is hit, this array is directly updated). The global HashMap keeps track of all of these class-local arrays, which are collected and reset when coverage is dumped.
This approach can yield a significant speedup in coverage collection. As an example (measured on my laptop, not very scientifically/controlled, but to demonstrate the order-of-magnitude improvement): running pit to collect coverage on apache commons-math took 161 seconds using version 1.4.3. In comparison, collecting coverage with JaCoCo (and dumping coverage after each test) took 70 seconds. Using this patched version of pit, the same exact coverage collection took only 73 seconds.
Here's a source-level example of the instrumentation result:
CodeCoverageStore maintains a reference to all of these $$pitCoverageProbes arrays
and empties them out between each test.
Here is an argument for thread safety:
If two threads are racing to update the probe array, there will be no incorrect behavior - this is an idempotent operation.
If one thread is racing to update the probe array while another is racing to read it and dump coverage, then it is safe to assume that there is already no happens-before relationship between that line of code running and the test ending (since we dump coverage when the test ends), and there’s nothing that we can do to solve that, regardless of how we lock. That is to say, if a line is deterministically covered by a test then we guarantee that we will report it as covered; if a line is not deterministically covered by a test, then we do not guarantee that we will report it as covered (but again, this was already the case).