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.CancellationTokensasync теперь использует меньше памяти в некоторых сценариях, например, когда 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