Comments 11
static void Main(string[] args)
{
var firstList = new List<string>(args);
var secondList = new List<string>();
secondList.AddRange(args);
}
Хоть конструктор и принимает IEnumerable, но внутри будет каст к ICollection, затем проверка размера с выделением массива этого размера и вызов метода CopyTo.
AddRange работает аналогично, сначала при необходимости увеличивает размер до нужного, а затем копирует элементы из исходной коллекции. Удобен если в коллекции элементы уже есть.
p.s. Оба варианта не приводят к лишним аллокациям, а код выглядит приятнее в отличии от ручного копирования.
p.p.s. Актуально для .Net 5, .Net Core и .Net Framework
Ну, не совсем. Мы же не берём исходную коллекцию "as is", а изменяем её - для каждого элемента вызывается Clone
. То есть просто указать её в конструкторе или передать в AddRange
не выйдет. LINQ с Select
сюда тоже добавлять не хотелось понятно по каким причинам. :)
Отличная статья
Спасибо!
рассматривались ли варианты вроде NetFabric.Hyperlinq или StructLinq, что-бы максимально сохранить простоту LINQ и не переписывать всё на циклы
Нет, я про такое не слышал. На самом деле LINQ осталось довольно много - как я писал в статье, всё точно не переписывалось, запросы устранялись точечно по результатам профилирования. Я не скажу точно, сколько LINQ запросов было переписано на циклы, но это точно даже не несколько десятков.
В общем и целом LINQ по коду осталось много, просто теперь у нас есть лучшее понимание того, где его можно использовать достаточно спокойно, а где лучше подумать или сразу воздержаться. :)
Насчет
Count
(иAny
) загляните в исходники LINQ - он достаточно хитер и если видит чтоIEnumerable
это на самом делеList
илиArray
то вызовет их свойстваCount
иLength
напрямую.
Безпредикатная версия Count
- да, я как раз приводил имплементацию в статье. В Any
такого нет. Ссылка на referencesource. В .NET завезли, похоже. Ссылка.
Неужели чтобы догадаться что params передается как массив необходимо было дизассемблировать в IL? Да он же в сигнатуре даже объявлен как массив.
Люблю в IL лазить, чтобы прояснять для себя разные моменты. Например, про передачу явного null
(что он не заворачивается), на оптимизации вызовов без аргументов тоже было интересно посмотреть. Про атрибут не знал. Да и в общем, думаю, кому-то может быть интересно, что там под капотом.
collection.Where(predicate).Skip(12).Any()
Лично мне не очень нравится читаемость этого кода (может просто не привык) + создание итераторов.
Что произошло при вызове метода с params? Выделил память (инкремент счётчика), заполнили, использовали и забыли. Далее GC (внимание) проходит по живым, достижимым объектам, среди которых нашего params массива уже нет. GC компактит кучу и перетирает память выделенную под params так никогда и не узнав, что он вообще существовал.
То есть непонятно зачем вы оптимизировали временные объекты если их создание и удаление это (почти) бесплатная операция? GC (почти) никогда эти объекты не увидит и не тронет. Все ваши оптимизации привели к тому, что снизилось давление на кучу, реже стал запускаться full GC, но он все также тратит много времени на эти запуски. Не то, чтобы это вообще было бессмысленно, но такое.
Гораздо веселее разобраться почему GC тратит время на обход и компакт кучи. Если модель заточить под GC, то беспокоиться о params и итераторах уже не нужно. Я хочу сказать, что создавать сотни тысяч и миллионы объектов в секунду это не проблема, если они живут секунды. А вот напихать в кеш кучу развесистых изменяемых объектов это прямой путь к тормозам.
Оптимизация .NET приложений: большой результат маленьких правок