After extensive debugging we found out that time spent in Garbage Collection was very high, especially when doing Document/Pdf related issues.
After doing some testing on the Spire.Doc code and decompiling the Spire.Doc libraries we found out that your library calls GC.WaitForPendingFinalizers and GC.Collect in the Dispose of Spire.Document. This has a severe impact on the performance of our application.
The offending code decompiled from ILSpy is:
- Code: Select all
case 0:
{
ᝋ = true;
\u1712();
GC.WaitForPendingFinalizers();
GC.Collect();
short num = 0;
num3 = num;
goto default;
}
I also found more occurences of GC.Collect calls in the code base (e.g. PdfDocument).
I would only have expected a GC.SuppressFinalize in the Dispose/Close (altough I'm not sure there is a finalizer in this class).
I made a very small test application to demonstrate the difference.
- Code: Select all
var sw = new Stopwatch();
sw.Start();
Parallel.For(1, 5000, (i) =>
{
using (var doc = new Spire.Doc.Document())
{
}
});
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalSeconds);
sw.Reset();
sw.Start();
Parallel.For(1, 5000, (i) =>
{
var doc = new Spire.Doc.Document();
});
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalSeconds);
the version without the using (Dispose) is twice as fast, but imagine what this does in a much more complex application with much more objects in memory.
I also have a screenshot from the Visual Studio diagnostics tools from the code above, showing the massive amount of forced Garbage Collects.
During the second run only 1 collect is triggered, by the CLR itself (a GEN1 collection).
Moreover, GC.Collect forces ALL generations to be collected, never mind the WaitForPendingFinalizers.
We really would like to urge you to remove these GC.Collect calls from your library as this is very unexpected behaviour from a library class. Or if you are unwilling at least provide us the means to turn them off.