Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ


Choose your language

InfoQ Homepage News .NET Core Runtime and Base Class Library Performance

.NET Core Runtime and Base Class Library Performance

Microsoft has announced that performance improvements have been made to the runtime and base class libraries in .NET Core.  While the performance improvements to ASP.NET Core have received more publicity, these improvements are just as important.

Some of the more significant changes are found in ten areas: collections, LINQ, compression, cryptography, math, serialization, text processing, file I/O, networking, and concurrency.  As with any set of performance changes, how they will affect any specific application depends on the exact use patterns. The following discussion only lists some highlights to give you an idea of what is being done. Many of these changes are based on open source pull requests. Important changes can thus be implemented that would not be worth the effort for Microsoft because the overall impact would be minimal. Yet these changes could be very important for significant segments of developers.

Collections are used extensively in almost any application. Many operations have been improved by streamlining them or reducing their complexity.  Some of these improvements are due to eliminating overhead, such as streamlining operations to enable better inlining, or reducing instruction count. SortedSet<T>'s constructor was fixed since the original was written in a simple way that did not scale well when handling duplicates. Min and Max for SortedSet<T> did not have to walk the entire tree. List<T>.Add got faster. Improvements were not just made to System.Collections.Generic, but other namespaces such as System.Collections.Concurrent. ConcurrentQueue<T> and ConcurrentBag<T> were basically rewritten. LINQ users should also see improvements just from the improvements in collections.

In addition, however, LINQ itself has been the subject of performance improvements. Many operators have been rewritten for .NET Core to reduce the number and size of allocations as well as simplifying the algorithms. For example, Enumerable.Concat makes sure that concatenating multiple Enumerables grows linearly, not exponentially. ToList and Select operators have been streamlined to reduce allocations and the use of delegates and interfaces, as well as minimizing field reads and writes and avoiding copying. Enumerable.ToArray now better manages internal buffers.

Unlike the memory manipulations in collections and LINQ, Compression is generally CPU bound. For example, DeflateStream now uses a native decompression library. The native compression library has also been optimized.

In cryptography, SHA256.Create uses a native implementation such as CNG on Windows, or OpenSSL on Unix.

Math operations have seen improvements in the operations of BigInteger.

Binary serialization is CPU, data, and memory intensive. BinaryFormatter has been added in .NET Core 2.0 with changes that allow an O(N) algorithm to be used for longer before switching to an O(N2) algorithm. Now larger serialized inputs can be processed more efficiently.

For text processing, Regex.IsMatch is now more efficient in memory allocation, and therefore garbage collection because of a change to the way data is cached. WebUtility.UrlDecode no longer decodes input that does not actually need to be decoded. Loop optimizations have been made to some built-in Encoding derived types.

String manipulation has also seen quite a few improvements. Enum.Parse is more efficient in memory use and therefore garbage collection. Various ToString methods have been improved. The String class itself has been improved in methods such as IndexOf and StartsWith.  Given that strings are often used ubiquitously in an application, such improvements should have enormous impact.

File I/O has been improved. FileStreams using asynchronous reading and writing are now more efficient.

Lower levels of the network stack have been improved. Asynchronous sockets now allow such operations that actually complete synchronously to avoid the costs of asynchronous completions. These kinds of changes have large impacts on the higher level networking functions that use socket primitives. Work on higher level networking classes such as NetworkStream and SslStream have also been a source of improvement.

Changes to concurrency and parallelism have been undertaken. Many .NET applications use the ThreadPool class. For example, QueueUserWorkItem replaces the global queue with one that involves less synchronization and allocation with a resulting large reduction in garbage collection. Synchronization primitives such as SpinLock have been worked on. The SpinLock.TryEnter method has been improved to fail faster in the case where the lock cannot be taken immediately. This improvement can result in significant improvements on hot paths that use this class.

There have been about one thousand merged pull requests in this round of performance improvements with much more work to be done. In general, performance improvement will be a bigger priority in terms of adding performance based APIs as well as improving existing libraries.

BenchmarkDotNet has run some independent tests on these changes.

Rate this Article