I am currently investigating some severe performance drops in an application.
Performance drops are of a strange kind - several iterations in a row work quite fast, but then there are one iteration that takes much more time to complete. This application works with graphics, so it looks very annoying.
Please, take a look at the following code.
while (true)
{
var rng = new Random(1);
var concurrenBag = new ConcurrentBag<ICollection<(int X, int Y)>>();
Parallel.For(0, 20000, i =>
{
var entry = new List<(int X, int Y)>(); // essentially, this is what's going on:
var r = rng.Next(0, 3); // around 20k handlers return coordinates of pixels to redraw
for (var j = 0; j < r; j++) // sometimes there are null entries, sometimes 1, more often 2
{ // all entries are added to concurrent bag
entry.Add((j, j * j));
}
if (entry.Count == 0)
entry = null;
concurrenBag.Add(entry);
});
var sw = Stopwatch.StartNew();
var results = concurrenBag.ToList().AsParallel().Where(x => x != null).SelectMany(x => x).Distinct().ToList(); // this is where severe performance drops occur from time to time
var time = sw.ElapsedMilliseconds;
Console.WriteLine($"CB count: {concurrenBag.Count:00000}, result count: {results.Count:00}, time: {time:000}");
//Thread.Sleep(1000);
}
This code produces the following results:
CB count: 20000, result count: 02, time: 032 <- this is fine, initialization and stuff
CB count: 20000, result count: 02, time: 004
CB count: 20000, result count: 02, time: 014 <- this is not fine
CB count: 20000, result count: 02, time: 003
CB count: 20000, result count: 02, time: 004
CB count: 20000, result count: 02, time: 004
CB count: 20000, result count: 02, time: 003
CB count: 20000, result count: 02, time: 015 <- every couple of frames it happens again
CB count: 20000, result count: 02, time: 003
CB count: 20000, result count: 02, time: 019
CB count: 20000, result count: 02, time: 004
CB count: 20000, result count: 02, time: 004
CB count: 20000, result count: 02, time: 003
CB count: 20000, result count: 02, time: 014
CB count: 20000, result count: 02, time: 003
CB count: 20000, result count: 02, time: 004
CB count: 20000, result count: 02, time: 003
CB count: 20000, result count: 02, time: 008
CB count: 20000, result count: 02, time: 003
CB count: 20000, result count: 02, time: 004
CB count: 20000, result count: 02, time: 011
CB count: 20000, result count: 02, time: 003
CB count: 20000, result count: 02, time: 003
CB count: 20000, result count: 02, time: 004
I believe you got the idea. In real application every "good" iteration takes around 10-15 ms, and those slow iterations occur every 6-8 iterations and take up to 150ms or something like that.
I honestly thought something is very wrong with my business logic, but you can run the example above and get quite the same results. I'm guessing now that there is something wrong with the way I utilized Parallel.For
, AsParallel()
or ConcurrentBag
, but I have no idea what's wrong exactly.