Pull to refresh
32K+
28
107
Rating
30
Subscribers
Send message

А чё, так можно было? Три места в.NET, которые не развидеть

Level of difficultyMedium
Reading time5 min
Reach and readers9.2K

Пустой наследник Random работает медленнее родителя. string[] и object[] оказываются одним объектом. А Matrix4x4 до сих пор держит шестнадцать полей float вместо четырёх векторов.

Ни одно из трёх уже не переделать, и на то есть причины.

Читать далее

А чё, так можно было? Опечатка в исходниках .NET, которой больше 20 лет

Level of difficultyMedium
Reading time5 min
Reach and readers6.8K

В System.Random кто-то двадцать лет назад написал 21 вместо 31. Опечатку заметили, разобрали и оставили как есть — исправить, и new Random(42) начнёт выдавать другие числа.

А ещё new Random() и new Random(42) — вообще разные генераторы. Разница на некоторых методах доходит до 10 раз.

Читать далее

А чё, так можно было? Три недооценённых атрибута.NET

Level of difficultyMedium
Reading time5 min
Reach and readers11K

Три атрибута, которые меняют результат компиляции. Один запускает метод раньше Main. Второй передаёт текст выражения вместо результата. Третий ускоряет stackalloc в 50 раз.

Все три описаны в документации и применяются внутри .NET. А в рабочем коде я их ни разу не видел.

Читать далее

А чё, так можно было? int == -int

Level of difficultyMedium
Reading time5 min
Reach and readers7.6K

Есть число, которое не меняется от смены знака. Минус перед ним ничего не делает, а модуля у него не существует: Math.Abs бросает исключение.

Это int.MinValue. И это ещё не всё, что он умеет.

Читать далее

А чё, так можно было? Dictionary == List

Level of difficultyMedium
Reading time5 min
Reach and readers8.5K

Ключ-структура из числа и строки. Поиск по словарю на 10 000 таких ключей занимает 74 975 микросекунд. Тот же ключ, объявленный record struct, — 3,354 микросекунды.

Разница в 22 354 раза, и берётся она не из сравнения ключей.

Читать далее

А чё, так можно было? Приватное поле за 0,25 наносекунды

Level of difficultyMedium
Reading time6 min
Reach and readers13K

Чтение приватного поля через рефлексию занимает 8,96 наносекунды и выделяет 24 байта в куче. Тот же доступ без рефлексии — 0,25 наносекунды и ноль байт.

Разница в 35,8 раза, а на .NET 8 она доходила до 108. Способ появился в .NET 8, применяется внутри библиотеки классов и работает без генерации кода.

Читать далее

А чё, так можно было? Недокументированный C#

Level of difficultyMedium
Reading time7 min
Reach and readers19K

В C# есть четыре ключевых слова, которых нет в документации. Компилятор их принимает.

Одно из них собирается на любой машине, а выполняется только на Windows. На Linux та же сборка падает. Дальше — как объект меняет свой тип на ходу и что на самом деле происходит при вызове метода у пустой ссылки.

Читать далее

А чё, так можно было? Порядок полей = 1,8× к скорости

Level of difficultyMedium
Reading time5 min
Reach and readers6.3K

Класс без единого поля занимает 24 байта, с двумя полями int — те же 24, а с тремя — уже 32.

В структуре достаточно поменять местами два поля, и её размер уменьшится с 24 байт до 16, а обход массива ускорится в 1,8 раза.

Читать далее

А чё, так можно было? +1 ключ = ×12 к скорости

Level of difficultyMedium
Reading time8 min
Reach and readers8.5K

В словарь добавляют 101 ключ — поиск занимает 8 763 наносекунды. Добавляют 102-й — 718.

Ключей стало больше, а времени в 12 раз меньше. Разбираемся, что происходит на 102-й вставке.

Читать далее

А чё, так можно было? new ≠ память

Level of difficultyMedium
Reading time13 min
Reach and readers9.4K

В .NET есть метод, который всегда возвращает false. Есть очистка списка, которая ничего не очищает. И есть new, после которого в куче пусто.

Разберем пять таких мест: что написано в исходнике, что от него остаётся в машинном коде и зачем так сделано.

Читать далее

Бенчмаркая сортировку строк: подстава с дефолтным компаратором — ×5

Level of difficultyMedium
Reading time6 min
Reach and readers7.2K

Сортировка строк без компаратора работает не так, как кажется, и заметно дольше.

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

Читать далее

Бенчмаркая LINQ: подстава с OrderBy — одно условие и полная сортировка

Level of difficultyMedium
Reading time8 min
Reach and readers9.7K

Одни вызовы после OrderBy проходят набор один раз, другие приводят к полной сортировке. По коду разницы не видно.

Давайте проверим счётчиком обращений к компаратору — на четырёх машинах и четырёх рантаймах.

Читать далее

Бенчмаркая Span.Sort: выбрал компаратор-структуру — и получил 88 байт на вызов

Level of difficultyMedium
Reading time10 min
Reach and readers9.2K

Перегрузка Span.Sort с компаратором-структурой должна была работать быстрее обычной. Замер показал обратное: памяти она расходует больше всех, а времени тратит больше, чем компаратор-класс — на .NET 8, 9 и 10.

В .NET 11 результат меняется, но не везде.

Читать далее

Бенчмаркая try/finally: один finally — и метод в 6,45 раза медленнее

Level of difficultyMedium
Reading time8 min
Reach and readers6.5K

Один try/finally внутри метода — и он работает в разы медленнее. Сам блок тут ни при чём, он не выполняет ни одной лишней инструкции.

Причина в решении JIT. В .NET 10 его поменяли — но не для всех методов и не при любых настройках.

Читать далее

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

Level of difficultyMedium
Reading time9 min
Reach and readers9.5K

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

Читать далее

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

Level of difficultyMedium
Reading time7 min
Reach and readers10K

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

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

Читать далее

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

Level of difficultyMedium
Reading time11 min
Reach and readers14K

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

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

Читать далее

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

Level of difficultyMedium
Reading time8 min
Reach and readers6.7K

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

Читать далее

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

Level of difficultyHard
Reading time14 min
Reach and readers12K

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

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

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

Читать далее

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

Level of difficultyMedium
Reading time14 min
Reach and readers12K

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

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

Читать далее
1

Information

Rating
49-th
Registered
Activity