-
Notifications
You must be signed in to change notification settings - Fork 388
Performance improvements #134
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,70 +2,61 @@ | |
using System.Collections.Generic; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.IO; | ||
using System.IO.Compression; | ||
|
||
using Coverlet.Tracker.Extensions; | ||
|
||
namespace Coverlet.Tracker | ||
{ | ||
public static class CoverageTracker | ||
{ | ||
private static Dictionary<string, List<string>> _markers; | ||
private static Dictionary<string, int> _markerFileCount; | ||
private static Dictionary<string, Dictionary<string, int>> _events; | ||
|
||
[ExcludeFromCodeCoverage] | ||
static CoverageTracker() | ||
{ | ||
_markers = new Dictionary<string, List<string>>(); | ||
_markerFileCount = new Dictionary<string, int>(); | ||
_events = new Dictionary<string, Dictionary<string, int>>(); | ||
AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit); | ||
AppDomain.CurrentDomain.DomainUnload += new EventHandler(CurrentDomain_ProcessExit); | ||
} | ||
|
||
[ExcludeFromCodeCoverage] | ||
public static void MarkExecuted(string path, string marker) | ||
public static void MarkExecuted(string file, string evt) | ||
{ | ||
lock (_markers) | ||
lock (_events) | ||
{ | ||
_markers.TryAdd(path, new List<string>()); | ||
_markers[path].Add(marker); | ||
_markerFileCount.TryAdd(path, 0); | ||
if (_markers[path].Count >= 100000) | ||
if (!_events.TryGetValue(file, out var fileEvents)) | ||
{ | ||
using (var fs = new FileStream($"{path}_compressed_{_markerFileCount[path]}", FileMode.OpenOrCreate)) | ||
using (var gz = new GZipStream(fs, CompressionMode.Compress)) | ||
using (var sw = new StreamWriter(gz)) | ||
{ | ||
foreach (var line in _markers[path]) | ||
{ | ||
sw.WriteLine(line); | ||
} | ||
} | ||
_markers[path].Clear(); | ||
_markerFileCount[path] = _markerFileCount[path] + 1; | ||
fileEvents = new Dictionary<string, int>(); | ||
_events.Add(file, fileEvents); | ||
} | ||
|
||
if (!fileEvents.TryGetValue(evt, out var count)) | ||
{ | ||
fileEvents.Add(evt, 1); | ||
} | ||
else if (count < int.MaxValue) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: I doubt that anyone who gets 2G hits on a line cares much if the counter stops then. :) |
||
{ | ||
fileEvents[evt] = count + 1; | ||
} | ||
} | ||
} | ||
|
||
[ExcludeFromCodeCoverage] | ||
public static void CurrentDomain_ProcessExit(object sender, EventArgs e) | ||
{ | ||
lock (_markers) | ||
lock (_events) | ||
{ | ||
foreach (var kvp in _markers) | ||
foreach (var files in _events) | ||
{ | ||
using (var fs = new FileStream($"{kvp.Key}_compressed_{_markerFileCount[kvp.Key]}", FileMode.OpenOrCreate)) | ||
using (var gz = new GZipStream(fs, CompressionMode.Compress)) | ||
using (var sw = new StreamWriter(gz)) | ||
using (var fs = new FileStream(files.Key, FileMode.Create)) | ||
using (var sw = new StreamWriter(fs)) | ||
{ | ||
foreach (var line in kvp.Value) | ||
foreach (var evt in files.Value) | ||
{ | ||
sw.WriteLine(line); | ||
sw.WriteLine($"{evt.Key},{evt.Value}"); | ||
} | ||
} | ||
} | ||
|
||
_markers.Clear(); | ||
_events.Clear(); | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using coverlet.testsubject; | ||
using Xunit; | ||
|
||
namespace coverlet.core.performancetest | ||
{ | ||
/// <summary> | ||
/// Test the performance of coverlet by running a unit test that calls a reasonably big and complex test class. | ||
/// Enable the test, compile, then run the test in the command line: | ||
/// <code> | ||
/// dotnet test -p:CollectCoverage=true -p:CoverletOutputFormat=opencover test/coverlet.core.performa ncetest/ | ||
/// </code> | ||
/// </summary> | ||
public class PerformanceTest | ||
{ | ||
[Theory(Skip = "Only enabled when explicitly testing performance.")] | ||
[InlineData(150)] | ||
public void TestPerformance(int iterations) | ||
{ | ||
var big = new BigClass(); | ||
|
||
for (var i = 0; i < iterations; i++) | ||
{ | ||
big.Do(i); | ||
} | ||
} | ||
} | ||
} |
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 throughn/2
items of then
items in the list on average. The number of events in a test can be described askn
, wherek
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 whenn
gets big, the value ofk
is irrelevant, so the time complexity of this operation is expressed asO(n^2)
in the big-O notation, i.e. quadratic time complexity.