Как броят на класовете в сборка влияе върху производителността?

Проектът, върху който работя, ще генерира код за голям брой класове - очакват се стотици до хиляди. Не е известно по време на генериране колко от тези класове действително ще бъдат достъпни.

Генерираните класове биха могли (1) всички да живеят в единичен сбор (или евентуално няколко сборки), който ще бъде зареден, когато започне процесът на използване на пръсти.

...или (2) мога да генерирам единичен асембли за всеки клас, подобно на Java, която компилира всеки клас в един *.class двоичен файл, и след това да измисля механизъм за зареждане на асемблите при поискване .

Въпросът: кой случай би довел до по-добра (памет и време) производителност?

Усещането ми е, че за случай (1) времето за зареждане и използваната памет са право пропорционални на броя на класовете, които съставят единичния монолитен сборник. OTOH, случай (2) идва със своите усложнения.

Ако знаете някакви ресурси, отнасящи се до вътрешността на зареждането на сглобки, особено какъв код се извиква (ако има такъв!?) и каква памет се разпределя (счетоводство за новозаредената сглобка), моля, споделете ги.


person Cristian Diaconescu    schedule 16.11.2012    source източник
comment
Опитахте ли да проверите с броячите на ефективността и профилирането? На пръв поглед бих казал, че първото е по-добро, тъй като режийните разходи за сборка не са малки както за зареждане, така и за потребление на памет.   -  person Adriano Repetti    schedule 16.11.2012


Отговори (2)


Опитвате се да разрешите несъществуващ проблем, зареждането на асембли е много силно оптимизирано в .NET.

Разбиването на голяма сглобка на множество по-малки без съмнение е най-лошото нещо, което можете да направите. Най-големият разход при зареждането на сборка е намирането на файла. Това е проблем при студен старт, CLR зареждащото устройство е затънало от бавния диск, записите в директорията трябва да бъдат извлечени и претърсени, за да се намерят дисковите сектори, които съдържат съдържанието на файла. Този проблем изчезва при топъл старт, когато данните за асемблирането могат да бъдат извлечени от кеша на файловата система. Имайте предвид, че Java също не го прави по този начин, тя пакетира .class файлове в .jar. .jar е грубият еквивалент на сборка.

След като файлът бъде локализиран, .NET използва средство на операционната система, за да направи действителното зареждане на данните за асемблиране много евтино. Той използва файл с карта на паметта. Което включва просто запазване на виртуалната памет за файла, но не четене от файла.

Четенето се извършва едва по-късно и се извършва от грешка на страницата. Характеристика на всяка операционна система за виртуална памет, работеща по заявка. Достъпът до виртуалната памет създава грешка на страницата, операционната система зарежда данните от файла и картографира страницата с виртуална памет в RAM. След което програмата продължава, без да осъзнава, че е била прекъсната от ОС. Това ще бъде трептенето, което произвежда тези грешки на страницата, то осъществява достъп до таблиците с метаданни в сборката, за да намери IL за метод. От което след това генерира изпълним машинен код.

Автоматична полза от тази схема е, че никога не плащате за код, който е в сборка, но не се използва. Трептенето просто няма причина да гледа секцията от файла, която съдържа IL, така че той всъщност никога не се чете.

И обърнете внимание на недостатъка на тази схема, използването на клас за първи път включва перфективно попадение поради прочетения диск. Това трябва да се плати по един или друг начин, в .NET дългът е изискуем в последния възможен момент. Ето защо атрибутите имат репутацията на бавни.

По-големите сглобки винаги са по-добри от много по-малки сглобки.

person Hans Passant    schedule 16.11.2012
comment
Добре, това се отнася до производителността, базирана на времето. Какво ще кажете за ефективността на паметта? Esp. за случая, когато само малък процент от класовете действително се докосват по време на изпълнение. Можете ли да прецените колко излишна памет ще нанесе всяка нова (неизползвана) дефиниция на клас в дефиниращия асембли? - person Cristian Diaconescu; 18.11.2012
comment
Както посочих, размерът на VM няма нищо общо с броя на използваните класове. Една гигантска сборка би била 10 мегабайта. Това отнема 0,5% от адресното пространство. Фъстъци. - person Hans Passant; 18.11.2012
comment
Най-добрият начин да разберете със сигурност е да тествате :) И така, ето какво направих. Създадох огромна сборка, съдържаща 10 000 класа (един огромен генериран изходен файл, който е около 200 MB). Времето, необходимо за зареждане на версията .dll (~45 MB) с Assembly.Load(): 0,05 секунди. И така, ето твърди доказателства, че сте били прави. - person Cristian Diaconescu; 17.12.2012

кой случай би довел до по-добра (памет и време) производителност

Имайки предвид, че компилаторът ще направи много оптимизация вместо вас, вариант 1 определено е правилният начин. Изглежда напълно пресилено да има отделна сглобка за клас. Не само това, вероятно ще намерите по-бързо зареждане на 1 голям модул, отколкото много малки.

Също така, това изглежда като преждевременна оптимизация, моят съвет би бил да се придържате към първата (разумна) опция и да разделите класовете на отделни сборки по-късно, ако видите необходимост.

person James    schedule 16.11.2012