Това е част от поредица от статии:

ToList()

Много често откривам използването на ToList()в края на всяка LINQ заявка. През повечето време това не е необходимо и може да има огромно влияние върху производителността.

Нека анализираме малък пример:

Този код записва в конзолата четните числа между 0 и 10. Можете да видите в SharpLab.io, че това върши работа.

Нека разширим ToList() до еквивалентен код:

ЗАБЕЛЕЖКА: ToList() и ToArray() реализациите имат някои страхотни оптимизации, но няма да разберете какво ги задейства, освен ако не погледнете в кода им.

Проверете в SharpLab.io дали резултатът е същият, но това ли наистина искахте? ToList()скрива допълнително List<T> разпределение, един foreachцикъл и копие на всеки елемент в списъка.

Всъщност ToList() не е необходимо за този случай...

Проверете в SharpLab.io дали кодът без ToList() извежда абсолютно същото.

производителност

Изпълнението на кода на BenchmarkDotNet за диапазони от 0, 500 и 1000 елемента показва следните резултати.

ЗАБЕЛЕЖКА: За бенчмарка изчислявам общата сума на елементите на последователността, вместо да извеждам към конзолата.

ЗАБЕЛЕЖКА: ToArray() е подобен на ToList(), но връща масив вместо списък и го включих в сравнителните тестове.

Не е изненадващо, че всички реализации са O(n), което означава, че времето за обработка се увеличава с броя на елементите в последователността.

Изненадващо, ToArray() е по-бърз, отколкото да не използва никакво преобразуване. Трябва да задейства една от оптимизациите.

По отношение на паметта, това е мястото, където неизползването на преобразуване прави огромна разлика и не е възможно да се оптимизират преобразуванията допълнително, тъй като данните трябва да са в паметта. За това са...

Преобразуването изисква заделяне на памет на куп, като необходимото количество нараства директно с броя на елементите в последователността.

Разпределенията на купчина ще задействат събирането на боклука. Ако разпределите малки количества и ги запазите за кратък период от време, те ще бъдат обработени от колекцията Gen 0, която е бърза, но не е безплатна. Ако разпределите голямо количество (›85 000 байта), те ще отидат директно в LOH (Large Object Heap), причинявайки фрагментирането му и забавяйки го.

Заключение

ToList() и ToArray() ще разпределят хийп памет, задействайки GC по-често и рискувайки да получат OutOfMemoryException, когато проектът се мащабира.

Трябва да ги използвате само когато е строго необходимо!