Комментарии 19
А еще List не очень дружит с мультипоточностью, поэтому и использую ConcurrentQueue вместо него.
Спасибо за хорошую статью, в грядущих оптимизациях буду иметь ввиду, на что обратить внимание)
Ого, экономия 15 нс на каждой инициализации списка! (сарказм)
А где самый очевидный вариант - построить реализацию IList на массивах?
Около 0 malloc на добавление\удаление\замену элемента.
Чтобы выделять большие куски памяти, освобождать и т.д.?
(Берем массив размера N, кончается - Добавляем еще массив размера N, и т.д.)
Если у вас .NET 8 или выше, то лучше использовать Collection expressions. Хороший баланс производительности и удобства.
// вместо трёх вызовов items.Add, будет один вызов билдера,
// который принимает Span<T> содержащий перечисленные элементы
List<int> items = [1, 2, 3];
ага, когда мы используем Collection expressions, за нас уже считается Capacity и сразу создается список нужного размера.
Вообще статья отличная и тема интересная. Но для Контура это очень мало, ожидания были другие. Я выделил себе час, только втянулся, завел бенчмарки. А потом все закончилось, оказывается здесь просто про Capacity. И что список это динамический массив.
Ну что сказать, могу только порадоваться за такие высокие ожидания от Контура в плане технических статей. Что-то более зубодробительное обычно пишет мой коллега, его статьи тоже есть в блоге. Там действительно часть разбираются какие-то хитрые кейсы.
Если говорить про себя, то я как-то уже писал, что мне нравятся такие простые (можно сказать "дешевые") оптимизации, которые будут полезны в любом проекте. И, мне кажется, с таких примеров легче заинтересовать разработчиков поглубже изучить язык, на котором они работают. Ведь действительно, всё так просто, нужно только указать Capacity. Из таких мелочей складывается понимание того, как работает твой код.
В любом случае, спасибо вам за оценку, я продолжу искать что-нибудь интересное для Хабра. А что-то более специфичное оседает у меня в блоге.
Есть ещё проблема с аллокациями, которая частично решена в .NET 10. Там среда может разместить массив на стеке, а не в куче, если будет убеждена, что это безопасно. А вот List гарантированно улетит в кучу (приходилось делать список значимым типом для борьбы с этой проблемой).
Вообще статья отличная и тема интересная.
Да не интересная.. она для тех кто вкатился через Unity, не вдупляет в то как работает в рантайм и не думает окрывать Рихтера. Тут из интересного только анализ в виде бенчмарков.

Контур конечно молодец, но статьи "а как работают списки" или "почему использование StringBuilder лучше обычного сложения строк".. оба кстати работают практически по одному паттерну, поэтому не вздумайте пилить статью еще и про это .. это какой-то детский сад. Так что согласен, я тоже ожидал тоже чего-то другого более глубокого и неочевидного.
Согласен, профессионал должен знать возможности своего инструмента.
Но чтобы такие профессионалы появлялись в индустрии, им сначала нужно дорастать до этого уровня, и на это требуется время.
Ваш комментарий выглядит так, будто должен заставить комплексовать разработчиков, которые не знали о такой возможности. Появление профессионалов требует спокойных разборов базовых тем, а не стыда за незнание.
Мои статьи для всех. Я показываю простые примеры оптимизаций, на которые обычно забивают и считают их незначительными. Я рассказываю реальную историю о том, где это пригодилось. И для меня важно, чтобы после статьи человек посмотрел на свой проект и нашёл очевидные возможности для улучшений.
Спасибо за дополнение. Бенчмарк с вами согласен и опять же говорит, что мы не тратим лишнюю память, ну и побыстрее это будет.
| Method | Runtime | Mean | Allocated |
|------------------------- |---------- |----------:|----------:|
| InitList10 | .NET 10.0 | 28.960 ns | 216 B |
| InitListWithSize10 | .NET 10.0 | 10.028 ns | 96 B |
| InitCollectionWithSize10 | .NET 10.0 | 7.703 ns | 96 B |Могли бы и синтаксис с {} переделать на спан наверное. А [] вообще не вводить потому что путается с размерностью массива
Кажется ключевая проблема не совсем в списках. Подобного рода динамические массивы вообще не предназначены для подписей, которые в памяти занимают место, сопоставимое с объёмом всей памяти. Ну просто банально потому, что всякие List и Dictionary дают нам скорость и удобство ценой очень сильного потребления памяти.
Как только разбрасываться памятью становиться расточительно — необходимы другие подходы и структуры данных. Тут можно применять как точное предвыделение памяти (как нас заставляет делать тот же zig). Так и всякие векторные трансформации: часто лучше разместить все данные этого "кеша" в одном большом массиве, — это и код упростит(внезапно), и перф даст. Печально, правда, что про векторные представления очень редко говорят :(
Можете пояснить про "разместить все данные этого кеша" - это как?
Ну, что делает это кеш нам не описали, поэтому ничего кроме рукомахательства я Вам не смогу сказать.
Основная идея в том, что вместо того чтобы создавать кучу маленьких списков, часто гораздо эффективнее будет создать один большой список. К этому списку обычно требуется также какая индексная структура данных, но это в среднем наоборот ускоряет код. Это нам немного ломает ооп-шные абстракции, но зато открывает путь к весьма интересным оптимизациям.
Наверное каноничным примером подобного преобразования является СНМ. В ней мы вместо того, чтобы хранить множества как отдельные структуры данных, складывается все множества в одну огромную структуру данных — но это даёт нам непревзойдённую эффективность объединения множеств почти за константу.
Если говорить из современных инкарнаций, то подобное преобразования можно найти в AoS -> SoA(struct of arrays), ecs и в целом в data-oriented programming.
Можно было бы ещё сравнить с immutable и frozen
По Imutable и Frozen есть пара интересных статей:
Заглядываем под капот FrozenDictionary: насколько он быстрее Dictionary и почему
Самый простой и подробный гайд по конкурентным коллекциям в C#
Поэтому тут я не стал повторяться.
Информация
- Сайт
- tech.kontur.ru
- Дата регистрации
- Дата основания
- Численность
- свыше 10 000 человек
- Местоположение
- Россия
- Представитель
- Диана
Создаёте списки в C#? Ну тогда у вас могут быть проблемы