25 способов эффективно использовать .NET
Поддержать канал можно тут: https://sponsr.ru/sharp_heppard
Post #121
2.43K
TryGetNonEnumerated #память
Хотелось бы напомнить про такую банальную, но весьма полезную оптимизацию, как создание списка с заранее известным размером.
Напомню, что первоначально
Однако, увы, некоторые методы возвращают
Это точно не
В нашем случае он может быть применён вот так:
Почему этот подход не используется в конструкторе того-же List’a (который принимает IEnumerable) и в его же методе
P.S.: Для желающих посмотреть, что метод TryGetNonEnumerated действительно ничего не перебирает, а просто возвращает значение - в комментариях есть бенчмарк.
Хотелось бы напомнить про такую банальную, но весьма полезную оптимизацию, как создание списка с заранее известным размером.
Напомню, что первоначально
List<T> создаётся с внутренним массивом размера 0. При последующем добавлении элементов происходит проверка, и, если места не хватает, внутренний массив расширяется на свой размер, умноженный на 2. «Расширение», в данном случае, означает, что создаётся новый массив, а содержимое старого массива копируется в новый. Таким образом, если мы заранее создадим List с внутренним массивом в 200 элементов, то мы избежим аллокаций шести массивов - это весьма солидно .Однако, увы, некоторые методы возвращают
IEnumerable<T>, из которого весьма проблематично узнать размер. Да, за IEnumerable<T> может скрываться любая из коллекций, имплементирующая ICollection<T>, и тогда проблем с выяснением первоначального размера нашей коллекции нет. Но что если нам пришёл результат чего-то вот такого?
_data = array // первоначальный массив
.Skip(1)
.Take(Count / 2)
.Order()
.Select(static i => i * i % 10 == 0 ? "да" : "нет");
Это точно не
ICollection<string>, то есть выяснить размер мы не сможем. Вернее, всё-таки сможем. Если заглянуть в недра .NET, то мы узнаем, что это некий Enumerable.SelectIPartitionIterator, который, на наше счастье, реализует внутренний интерфейс IIListProvider. Чтобы попытаться воспользоваться его методом GetCount, нам поможет метод TryGetNonEnumeratedCount, который появился аж в .NET 6.В нашем случае он может быть применён вот так:
var capacity = _data.TryGetNonEnumeratedCount(out var count)
? count
: ваша_эвристическая_константа;
var list = new List<string>(capacity);
list.AddRange(_data);
Почему этот подход не используется в конструкторе того-же List’a (который принимает IEnumerable) и в его же методе
AddRange - загадка. Наверное, у коллег пока просто не дошли руки.P.S.: Для желающих посмотреть, что метод TryGetNonEnumerated действительно ничего не перебирает, а просто возвращает значение - в комментариях есть бенчмарк.
- 👍 24
- ❤ 5











