Обновить
32K+
22

Пользователь

77,1
Рейтинг
17
Подписчики
Отправить сообщение

Бенчмаркая Enumerable.Chunk: почему батчей меньше, а проход до ×2,7 дольше

Уровень сложностиСредний
Время на прочтение9 мин
Охват и читатели9.2K

Chunk делит коллекцию на массивы — одна строка кода. Но есть размер чанка, после которого он замедляется в разы при тех же данных и той же памяти. А на массиве и на List<int> внутри разный код, и в самой строке этого не видно.

Читать далее

Бенчмаркая StringBuilder: подстава на длинном тексте

Уровень сложностиСредний
Время на прочтение7 мин
Охват и читатели10K

StringBuilder делит текст на чанки по 8000 символов — вчетверо ниже порога кучи больших объектов. Запас такой, что попасть туда невозможно.

Но чанк на 400 КБ в этой куче возможен и получить его можно разными способами.

Читать далее

Бенчмаркая ArrayPool: подстава при копировании потоков — 131 072 байта в LOH

Уровень сложностиСредний
Время на прочтение11 мин
Охват и читатели13K

ArrayPool используют ради экономии на аллокациях. Но есть размеры, где он делает обратное: массив уходит в LOH, а через new остаётся в нулевом поколении.

Один такой размер зашит в .NET по умолчанию — им копируются потоки. Проверил на четырёх машинах и трёх рантаймах.

Читать далее

Бенчмаркая ZLinq: один IEnumerable в сигнатуре — и .NET 10 быстрее библиотеки в 3,9 раза

Уровень сложностиСредний
Время на прочтение8 мин
Охват и читатели6.4K

ZLinq — замена LINQ без аллокаций. На .NET 8 и .NET 9 время одинаковое. На .NET 10 иначе: массив тот же, но если параметр объявлен как IEnumerable, foreach перебирает его в 2,58–3,89 раза быстрее ZLinq. Причина видна в машинном коде, память замерена отдельно.

Читать далее

Бенчмаркая System.Text.Json: те же данные, те же настройки, до ×4,3 разницы

Уровень сложностиСложный
Время на прочтение14 мин
Охват и читатели11K

В документации к PropertyNameCaseInsensitive есть предупреждение про накладные расходы, но не сказано, когда они появятся. Замерил на четырёх машинах, трёх рантаймах и трёх размерах входного JSON: пока через настройки идёт один регистр ключей, флаг не добавляет ничего — 0,92–1,09. Политика именования camelCase тоже.

Разницу до ×4,3 даёт другое: какие ещё написания этих ключей прошли через настройки раньше. Два экземпляра JsonSerializerOptions, созданные через new с одинаковыми полями, делят один кеш имён.

Внутри: пять историй с таблицами по четырём машинам, листинги из dotnet/runtime, предел кеша в 64 записи и веб-настройки, на которых эта разница не видна.

Читать далее

Бенчмаркая Regex: конструктор показал 10 мкс, первый вызов — 1307

Уровень сложностиСредний
Время на прочтение14 мин
Охват и читатели12K

Проверить строку регулярным выражением в .NET можно по-разному, но совет всегда один: включите RegexOptions.Compiled или возьмите генератор. Замеры это подтверждают.

А вот подготовка выражения оказалась не там, где её меряют: конструктор занимает 10 микросекунд, а первая же проверка на этом объекте — ещё 1307. Разобрался, куда уходит разница, и заодно нашёл границу, на которой статический Regex.IsMatch резко замедляется.

Читать далее

Бенчмаркая FrozenDictionary: заменил Dictionary — получил от ×0,76 до ×1,9

Уровень сложностиСредний
Время на прочтение13 мин
Охват и читатели9.8K

FrozenDictionary сделан под словари, которые заполняют один раз при старте, а дальше только читают. Замерил на четырёх машинах и трёх рантаймах — разброс вышел от ×0,76 до ×1,9, и зависит он не от версии .NET, а от того, сколько ключей и как они устроены.

Разобрал, куда уходит время в поиске по строке, вытащил из рантайма имена реализаций, которые он подбирает под конкретный набор ключей, и посмотрел в дизасме, за счёт чего Frozen выигрывает — и почему на маленьких наборах проигрывает.

Читать далее

Бенчмаркая ключи Dictionary: забыл IEquatable — получил ×5 и 96 байт на каждый поиск

Уровень сложностиСредний
Время на прочтение7 мин
Охват и читатели10K

Структура в роли ключа Dictionary — частый случай. Забыл IEquatable — и каждый TryGetValue в 4,4–5,7 раза медленнее, плюс 96 байт в куче на каждый поиск.

Проверил на четырёх машинах и трёх рантаймах, снял дизасм FindValue и разобрал, откуда берутся ровно три упаковки, почему override Equals не спасает и при чём тут хэш record struct. Сам рантайм этот разрыв не закроет — и это не баг.

Читать далее

Бенчмаркая регресс LINQ: обещали −19%, на четырёх машинах намерил +31%

Уровень сложностиСредний
Время на прочтение5 мин
Охват и читатели11K

Уважаемые читатели, в этой статье я хочу рассказать о проверке обещанного регресса LINQ в .NET 10 — и представить свои выводы.

В dotnet/runtime с лета висит issue #117717: после перехода на .NET 10 метод с First(предикат) просел к девятке — у автора issue на Intel 13-го поколения до 19%, у Энди Эйерса из JIT-команды на Zen 4 порядка 10%. Причину команда назвала сама: связка PGO и инлайнинга, делегат предиката перестал инлайниться.

Решение тоже озвучили: в десятке чинить не будем — сломаем другое, переносим в .NET 11. Был частичный фикс (PR #117816), после которого issue переоткрыли как #119425. Десятка при этом LTS, и в комментах закономерный вопрос: обновляться на десятку или ждать одиннадцатую.

Читать далее

Бенчмаркая Sum: ускорил циклом — замедлил в ×4,7

Уровень сложностиСредний
Время на прочтение8 мин
Охват и читатели11K

Уважаемые читатели, в этой статье я хочу рассказать о том, что происходит внутри values.Sum() в современном .NET — там нашлись векторные инструкции, контроль переполнения и список процессоров, которым рантайм намеренно ограничивает ширину вектора, — и представить свои выводы.

В прошлых статьях серии самописные циклы уже проигрывали BCL в поиске по строке, JIT сам выкидывал проверки границ, а foreach прятал аллокации. Тут случай интереснее: values.Sum() — это LINQ, который при оптимизации первым делом меняют на цикл.

Читать далее

Бенчмаркая поиск по строке: самописные циклы проигрывают от ×14 до ×154

Уровень сложностиСредний
Время на прочтение10 мин
Охват и читатели14K

Уважаемые читатели, в этой статье я хочу рассказать про поиск по строке и представить свои выводы. Началось с оптимизации: сравнил поиск символа циклом со string.IndexOf — и получил разницу в разы. Заодно выяснилось, что серверный Xeon с AVX-512 в этой задаче медленнее игрового десктопа с AVX2. Ниже разбор обоих фактов с дизасмом и замерами.

Будет четыре истории, и в каждой — вопрос, на который я искал ответ:

Читать далее

Бенчмаркая проверки границ: фикс, который шёл 8  лет, и проверка, которая жива до сих пор

Уровень сложностиСредний
Время на прочтение8 мин
Охват и читатели8.5K

Уважаемые читатели, в этой статье я хочу рассказать о проверках границ массива и представить свои выводы. Каждое обращение arr[i] обязано проверить индекс, иначе IndexOutOfRangeException — а JIT старается эти проверки убирать, когда может доказать, что выход за границы невозможен. Я взял несколько обычных циклов и посмотрел в машинный код на .net 8, 9, 10 и 11 одновременно. Результат разложился по версиям так, как я не ожидал.

Будет три истории:

Читать далее

Бенчмаркая foreach и энумераторы: аллокации, которые прятались 10 лет

Уровень сложностиПростой
Время на прочтение17 мин
Охват и читатели15K

Уважаемые читатели, в этой статье я хочу рассказать о скрытых аллокациях в обычном foreach и представить свои выводы. Тесты сделаны на .net 8, .net 9 и .net 10 одновременно, чтобы было видно, как одна и та же строчка кода ведет себя на разных рантаймах.

Все тесты сделаны с использованием BenchmarkDotNet, полный код представлен на гите, так что каждый может проверить результаты и сделать свои выводы.

Будет три истории, и все три про foreach:

Читать далее

Бенчмаркая Capacity 18 коллекций: как быстрей?

Уровень сложностиПростой
Время на прочтение5 мин
Охват и читатели3.1K

Уважаемые читатели, в этой статье я хочу рассказать о тесте 18 коллекций с и без capacity и поставить точку в данном вопросе. Тесты сделаны на .net 7 с использованием BenchmarkDotNet и представлен полный код для повторения. Базовые значения 1000 и 10_000.

В данной статье не буду сильно углубляться в коллекции, но немного напишу, что да как.

Основная цель понять, насколько важно задавать начальную емкость, даже примерно и насколько все страдает, если так не делать. Сразу отмечу, что на подавляющем большинстве увиденного мною кода практически всегда не задается capacity.

Часть коллекций используется для сравнения, так как емкость там не задается по ряду причин. Мы все их знаем, иногда или всегда используем и, как по мне, стоит глянуть в сравнении как что будет.

Да, не всегда важно, согласен, но секунда дела и улучшить памяти и скорость, чем плохо?

Кратко о коллекциях, где емкость не задается:

Читать далее

Бенчмаркая Array Reverse: как быстро перевернуть массив?

Уровень сложностиПростой
Время на прочтение12 мин
Охват и читатели5.1K

Уважаемые читатели, в этой статье я хочу рассказать о небольших тестах получения обратного массива и представить свои выводы. Тесты сделаны на .net 7.

Тестировал с использованием BenchmarkDotNet, так что каждый может проверить результаты и сделать свои выводы.

Сразу отмечу, что для тестов используется атрибут [GlobalSetup], что позволяет не переживать о стартовых данных, так как они будут «Executed once per each N value» и это нам и надо.

Для полной картины происходящего основные тесты идут на массивах с количеством элементов 1000, 10_000, 100_000, 1_000_000, 100_000_000, а самый последний с добавлением до 1_000_000_000 чисел.

Первые тесты связаны с обработкой исходного массива без получения нового, что не всегда верный выбор, но памяти почти всегда не требует (тесты покажут странность на крупной выборке).

Для старта используется обычный While, представленный в более удобной записи, чем аналоги на просторах сети:

Читать далее

Бенчмаркая строки и циклы: Replace, Split и Substring

Уровень сложностиПростой
Время на прочтение9 мин
Охват и читатели5.8K

Уважаемые читатели, в этой статье я хочу рассказать о небольших тестах со строками и представить свои выводы. Тесты сделаны на .net 7.

Про строки написано немало, поэтому слишком углубляться в их особенности не буду, лишь напомню, что строки в реальности могут быть изменяемыми с использованием различных ухищрений.

Все тесты сделаны с использованием BenchmarkDotNet, так что каждый может проверить результаты и сделать свои выводы.

Хочется начать с string.Replace, который проверяется разными вариантами, начиная с базового.

Читать далее

Информация

В рейтинге
94-й
Зарегистрирован
Активность