-
Notifications
You must be signed in to change notification settings - Fork 388
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
Performance improvements #134
Conversation
This is done by aggregating hits in the CoverateTracker directly, rather than just recording events in files, and by avoiding the former quadratic complexity when translating these event files into hits on lines and branches.
It can be useful to see in the IDE where the test failed. (dotnet watch + Coverage Gutter) |
Oh wow! This improvement is much welcome. Will take a deeper look tomorrow |
Also, if you've got time can you just give me a brief overview of the major bottle necks you found? |
int end = int.Parse(info[3]); | ||
for (int j = start; j <= end; j++) | ||
{ | ||
var line = document.Lines.First(l => l.Number == j); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line was the core of the bottleneck (together with the similar line doing document.Branches.First
above. Having the lines in a list means that it finding the correct line for an event will have to search through n/2
items of the n
items in the list on average. The number of events in a test can be described as kn
, where k
is the number of times the average line is executed during the test.
Putting that together means that processing all events in the test will take (k/2) * n^2
operations. In complexity theory the factor is not interesting since when n
gets big, the value of k
is irrelevant, so the time complexity of this operation is expressed as O(n^2)
in the big-O notation, i.e. quadratic time complexity.
@tonerdo I've added a comment to the diff highlighting the lines causing the main bottleneck. The PR addresses that by using a dictionary to tally the event counts. According to The second part of this PR moves this tallying of events from the post-processing step into Further optimisation possibilities worth investigating could be:
|
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\coverlet.testsubject\coverlet.testsubject.csproj" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can move BigClass.cs
directly into this project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might have misunderstood something then, as I thought only the dependencies of the unit tests got instrumented, not the unit test DLL itself. Is that not the case? The purpose of having this test subject DLL was to get BigClass instrumented when running the performance test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My mistake @petli. Thanks for the clarification
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The class do need a comment block explaining why it is where it is, I'll fix that.
{ | ||
fileEvents.Add(evt, 1); | ||
} | ||
else if (count < int.MaxValue) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if count
is less than int.MaxValue
? Shouldn't we try a long
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I grabbed this code (but I see I changed the condition slightly) from the former site of the hit counting:
https://github.com/tonerdo/coverlet/pull/134/files/1f4da61d7aaa444ff1857f65ddddaec6f01e1f95#diff-9eb1afa9bd8141e48d456c120df924caL189
I doubt that anyone who gets 2G hits on a line cares much if the counter stops then. :)
Performance improvements
Bumps [coverlet.collector](https://github.com/coverlet-coverage/coverlet) from 1.3.0 to 3.0.1. #Release notes *Sourced from [coverlet.collector's releases](https://github.com/coverlet-coverage/coverlet/releases).* > ## v3.0.0 > * [#131](coverlet-coverage/coverlet#131) makes a slight change to the Coverlet JSON format > * 807f7b1bd5bea8158ffff343d5511cd16e0da9a0 uses a separate `coverlet.tracker` assembly to hold tracking code > * [#128](coverlet-coverage/coverlet#128) adds support for assemblies with `.exe` extension > * a1f18b4156374f3398d704e898ec58c7c6c64bf8 improves identifying compiler generated types > * [#134](coverlet-coverage/coverlet#134) adds considerable coverage tracking performance improvements > > ## v2.0.1 > * [#102](coverlet-coverage/coverlet#102) fixes issues with NUNIT3 Test adapter ([#101](coverlet-coverage/coverlet#101)) > * [#104](coverlet-coverage/coverlet#104) shows overall averages as part of final console output > * [#112](coverlet-coverage/coverlet#112) adds support for standard `ExcludeFromCodeCoverage` attribute to specify types and methods to exclude from code coverage. Deprecates `ExcludeFromCoverage` attribute > * coverlet-coverage/coverlet@7f190e4 prevents Opencover and Cobertura output generated at the same time from overwriting each other ([#111](coverlet-coverage/coverlet#111)) > * [#116](coverlet-coverage/coverlet#116) strongly signs the Coverlet assembly and aims to fix [#40](coverlet-coverage/coverlet#40) > > ## v2.0.0 > * [#78](coverlet-coverage/coverlet#78) adds support for generating multiple report formats in a single run > * [#73](coverlet-coverage/coverlet#73) improves branch coverage support and output formats* > * coverlet-coverage/coverlet@d2effb3 shows method coverage in summary output > * [#88](coverlet-coverage/coverlet#88) improves disk usage by using gzip compression > * [#93](coverlet-coverage/coverlet#93) adds `ThresholdType` property that allows you to specify the coverage type to apply the `Threshold` property to > * coverlet-coverage/coverlet@ebedd70 renames `Exclude` property to `ExcludeByFile`* > * coverlet-coverage/coverlet@9ed0864 supports using filter expressions to exclude assemblies, namespaces or types. Uses the `Exclude` property* > * [#99](coverlet-coverage/coverlet#99) adds improvements to evaluation of filter expressions > > `*` - Backwards incompatible change #Commits - See f...
Fixes #120 see comments in that issue on the performance improvements here.
One possibly breaking change here is that it no longer periodically writes files (as there's no real need for it) but that also means that if the process exit callbacks aren't called for some reason, no results at all will be available. A periodic flush could be added based on number of events, but since such a partial result is not very useful, I think it would be better to look for ways to ensure that the results always are written correctly.
To test the performance change, just checkout commit b3c080c that just contains the new performance test. The changes are in the second commit.