dotNET / Производительность dotNET / Performance
Скоро в свет выйдет .NET Core 2.1. Осталось сделать меньше 1% задач. Конечно, оставшиеся 5% проекта занимают 95% времени, но будем оптимистами :)
Далее будет краткое резюме статьи Performance Improvements in .NET Core 2.1.
Если вы готовы прочитать эту интересную, но огромную статью — прочитайте. Я всё-таки пристрастен и расскажу о том, что интересно лично мне. А если не хотите читать и короткую…
TL&DR; Значительно (в 1.5-2 раза) улучшена производительность многих вещей, в том числе:
Span<T>
и Memory<T>
.сравнения внутри Dictionary
.async/await
.Socket
, SslStream
и HttpClient
.Далее немного про некоторые оптимизации.
Disclaimer:
Span<T>
и Memory<T>
Если вы не в курсе, что такое Span<T>
и Memory<T>
, рекомендую прочитать статью
C# - All About Span: Exploring a New .NET Mainstay.
Упрощенно, Span<T>
— это окно для куска памяти (и managed и unmanaged), чтобы не копировать лишний раз.
Концептуально — штука хорошая. Если честно, я на это пристально не смотрел и еще не пробовал. Поэтому воздержусь от комментариев.
В 2.1 добавили оптимизаций, связанных с “девиртуализацией”. Напомню — виртуальный вызов дороже статического. А новые оптимизации иногда позволяют заменить виртуальным вызов статическим. А статический, как вы наверняка помните, иногда можно вообще заинлайнить.
Оптимизировали EqualityComparer<T>.Default
— теперь, внутри вызова метода Equals
для некоторых типов не происходит виртуальный вызов GenericEqualityComparer<T>.Equals
.
То есть, если T
это Int32
, вызов работает быстрее. В 2.5 раза быстрее.
Как следствие, Dictionary<int,int>.ContainsValue
работает быстрее более чем в 2 раза.
Теперь, если структура реализует интерфейс, то подобное использование не приводит к боксингу:
А ещё и девиртуализация происходит.
Понятно, что вызов подобного кода (и только его) в цикле, мягко говоря, не самый распространенный случай…
Однако, отсутствие выделения памяти (48 байт) и девятикратное повышение скорости дает шанс неплохой оптимизации и в более жизненных сценариях.
В том числе, за счёт этой оптимизации async
стал быстрее.
ThreadStatic
стал быстрее в 1.4 раза.Monitor
(и lock
).ReaderWriterLockSlim
теперь лучше работает на большом количестве потоков.Timer
работает в 2 раза быстрее.CancellationTokenSource.CancellationTokens
async
теперь использует меньше памяти в некоторых сценариях, например, когда suspend’ится (в тесте из статьи — в 2 раза меньше памяти).В основном, за счет использования Span<T>
(строки в большинстве тестов порядка 180 символов):
String.Equal
быстрее в 1.5 раза.String.IndexOf(char)
быстрее в 2.5 раза.String.IndexOfAny
быстрее в 2 раза.String.ToLower/ToUpper
, когда есть что менять быстрее почти в 2 раза.String.ToLower/ToUpper
, когда нечего менять быстрее более чем в 2.5 раза (и нет лишнего выделения памяти).String.Split
и String.Concat
не так впечатляют, но в несколько раз снижено использование памяти.StringBuilder.AppendFormat
, а значит и String.Format
немного улучшены и по скорости и по памяти.StringBuilder.Append
для целых чисел работает в 2 раза быстрее и не использует дополнительную память (0 против 3 мегабайт в примере).Int32.ToString
быстрее почти в 2 раза.Int32.Parse
быстрее в 1.25 раза.Double.ToString
на Windows быстрее почти в 2.5 раза, а на Unix — в 7.5 раз.Socket
в некоторых сценариях на Unix быстрее в 2 раза.SslStream
на Unix лучше масштабируется.За кадром остались оптимизации:
Enum.HasFlag()
;CompareOrdinalIgnoreCaseHelper
;async/await
;BigInteger.ToString()
;DateTime.ToString()
;DateTimeOffset.ToString()
;Directory.EnumerateFiles()
;Dns.GetHostAddressAsync
;IPAddress.HostToNetworkOrder/NetworkToHostOrder
;new Uri()
;HttpClient
и HttpMessageHandler
;Rfc2898DeriveBytes
;Guid.NewGuid()
для Unix;Regex.Compiled
(с ним забавно вышло — в .NET Core 2.0 он просто игнорировался o_O);dotNET / Производительность dotNET / Performance