Откровенно говоря, большого желания спорить нет, устал я с тобой спорить, да и тон твой мене уже надоел порядком. Поэтому отвечу лишь на 2 пункта
VD>Чтобы было прикольнее пишу это сообщение с 40 параллельно запущенными тестами в каждом из который торчит тот самый не экономный грид с 10 000 записей каждый. параллельно рабатают еще около 40 процессов среди которых Судтия, Янус
Читать — разделяемая память в ОС Windows. При запуске n раз одного и того же приложения приличная часть памяти есть общая память.
VD>Ага. Но опять таки, как только ты возвращаешся к приложению, то система вынуждена отдавать страницы обратно. И обычно сделав минимизацию, возвратившить в приложение и чуть-чуть подергавшись в нем ты видишь тот объем памяти (физической) который действительно нужен приложению для эффективной работы. Отсальное — это баласт который или вообще не выделен физически процессу (не закомиченая память),
Вот на этом я особо хочу остановиться. Потому как эта фраза попросту демонстрирует твое непонимание самих основ вирт. памяти.
Что это за балласт, который вообще не выделен процессу (не закомиченная память) ?. Что это вообще такое ?
Незакоммиченным (а только зарезервированным) может быть только адресное простанство, а не память. И это называется резервированием адресного пространства и дерезервированием. Никакой памяти вообще при резервировании или дерезервировании АП не выделяется и не освобождается.. И сбрасывать этот резервированный регион на диск имеет смысл не более, чем сбросить на диск математическое пространство .
Резервирование или дерезервирование могут повлиять на VM Size — общий объем резервированных адресов. Но уж никак не на Working Set — потому что это объем страниц, в настоящее время находящихся в RAM.
Вот и все. Советую все же прочитать книги по основам ОС, чтобы чепуху не писать.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Резервирование или дерезервирование могут повлиять на VM Size — общий объем резервированных адресов. Но уж никак не на Working Set — потому что это объем страниц, в настоящее время находящихся в RAM.
Маленькое уточнение. Даже на VM Size (в смысле Task Manager) резервирование повлиять не может , так как это объем коммитированной памяти. Для получения объема резервированного пространства надо использовать , например, Process Viewer.
Здравствуйте, Pavel Dvorkin, Вы писали:
VD>>Чтобы было прикольнее пишу это сообщение с 40 параллельно запущенными тестами в каждом из который торчит тот самый не экономный грид с 10 000 записей каждый. параллельно рабатают еще около 40 процессов среди которых Судтия, Янус
PD>Читать — разделяемая память в ОС Windows. При запуске n раз одного и того же приложения приличная часть памяти есть общая память.
Если ты о коде dll-ек, то ты не совсем прав. Сама она, может быть и шарится, да только вот заjitеный код м/у процессами уже не шарится.
Здравствуйте, Max.Subpixel, Вы писали:
MS>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>Иными словами, ты хочешь сказать, что одно и то же значение WorkingSetSize в блоке EPROCESS в ядре для процесса означает разные вещи в зависимости от того, на каком языке писался исходный код ?
MS>Мне кажется вы зря иронизируете. MS>Вопрос не в языке, а в том, что в .NET есть GC, который умеет эффективно реагировать на просьбы подвинуться.
Вот , кстати, тот же простой пример.
.Net, Листбокс, 10000 строк
Запустили
Mem usage(т.е. Working set) — 8.736 Mв
VM Size (коммитированная память) — 8.972 Mb
Минимизировали
Mem usage(т.е. Working set) — 592 Кв
VM Size (коммитированная память) — 8.924 Mb
Т.е при минимизации система по своей инициативе урезает рабочий набор. Коммитированная память остается
практически без изменения, а Working Set она резко уменьшает.
Только вот .Net здесь ни при чем. Так все приложения себя ведут.
Mozilla
Mem Usage сейчас 20.832
VM Size — 22.532 Mb
Минимизировали
Mem Usage 1.516
VM Size — 22.508 Mb
Тот же эффект, даже более ярко выраженный.
А теперь Мозиллу развернем, но сначала посмотрим на Page Faults. Оно сейчас 25,078
Mem Usage 11.784 (полистать письма — увеличится)
VM Size — 22.404 Mb
Page Faults — 26,985
Page Fault — это обращеие к диску, так как больше брать неоткуда
(26985-25078) * 4Kb = 7628 Kb
В действительности больше. Дело в том, что Windows использует алгоритм загрузки страниц с кластеризацией. Иными словами, читается за 1 раз не страница, а блок последовательно лежащих.
Вот эти 8 Мб и перекачали с диска при максимизации. Их и откачали при минимизации до этого.
А теперь мой листбокс развернем
Page Faults
До разворачивания — 4924
После — 5171
(5171 — 4924) * 4 Kb = 988 Кв
Поменьше, намного меньше, но ведь мой листбокс далеко не Мозилла
Ну и Mem Usage растет понемногу.
В общем, никакой разницы я не вижу между управляемыми и неуправляемыми приложениями в этом плане
L>Если ты о коде dll-ек, то ты не совсем прав. Сама она, может быть и шарится, да только вот заjitеный код м/у процессами уже не шарится.
Вот насчет заjitеный код высказываться не буду, так как здесь некомпетентен. Меня не интересует, как там в самой .Net, а я просто говорю о процессе. А в нем шарятся все страницы, которые read/only и те страницы, которые явно или неявно на WRITE_COPY механизме (код,ресурсы) до попытки модификации страницы. Разумеется, речь идет о тех страницах, которые имеет смысл шарить. Страницы стека потока не шарятся
А jit-code, действительно, скорее всего не шарится, поскольку с точки зрения Windows это приватные страницы, которые вначале были данными (когда джиттер туда код загонял), а потом стали кодом.
Но вообще-то количество кодовых страниц всегда намного меньше, чем страниц данных. Одну хорошую bmp в ресурсы — и будет больше, чем весь код серьезного приложения
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Читать — разделяемая память в ОС Windows. При запуске n раз одного и того же приложения приличная часть памяти есть общая память.
Можешь хоть как "оче наш" заучить Рихтера и Русиновича, но разумность оценок от этого лучше у тебя не станет. В совренменных ОО-программах основная память тратится под объкты. Код обычно не привышает 10%. Так что разделять особо нечего. Да и не много программ имеет смысл запускать по десять раз.
VD>>Ага. Но опять таки, как только ты возвращаешся к приложению, то система вынуждена отдавать страницы обратно. И обычно сделав минимизацию, возвратившить в приложение и чуть-чуть подергавшись в нем ты видишь тот объем памяти (физической) который действительно нужен приложению для эффективной работы. Отсальное — это баласт который или вообще не выделен физически процессу (не закомиченая память),
PD>Вот на этом я особо хочу остановиться. Потому как эта фраза попросту демонстрирует твое непонимание самих основ вирт. памяти.
Моя версия — непонимание присутствует у тебя.
PD>Что это за балласт, который вообще не выделен процессу (не закомиченная память) ?. Что это вообще такое ?
PD>Незакоммиченным (а только зарезервированным) может быть только адресное простанство, а не память.
Ты выдрал цитату из контекста. Там еще два пункта было. Про незакомиченную память я сказал для полноты картины. Незакомиченное конечно адресное простанство.
PD> И это называется резервированием адресного пространства и дерезервированием. PD>Никакой памяти вообще при резервировании или дерезервировании АП не выделяется и не освобождается.. И сбрасывать этот резервированный регион на диск имеет смысл не более, чем сбросить на диск математическое пространство .
Ты удивишся, но закомиченная, но неиспользогванная память тоже на диск не сбрасывается.
PD>Резервирование или дерезервирование могут повлиять на VM Size — общий объем резервированных адресов. Но уж никак не на Working Set — потому что это объем страниц, в настоящее время находящихся в RAM.
Серьезно? А ты точно хорошо своего Русиновича прочел? Ты все напутал.
Я же большей частью говорил про ворксет и выделенную но неиспользованную память. После минимизации и востановаления ворксет приходит к 5-10 метрам. И как ты собирашся отделать что там в свопе, а что просто отдали системе? Ведь из 60 метров 40 могли быть просто неиспользованной закомиченой памятью. А 20 реально выделенной. Причем из этих 20 где нибудь 10 метров могут быть редко используемыми или вообще баластом (которому самое место в свопе).
Ты уперся в ворота и не хочешь признать банального факта. Дотнет отдает ресурсы системе если ей их нехватает. Он банально собирает мусор и уменьшает кучу. Посему смотреть на цифири получающиеся на машинах с 2 гигами ничем не занятыми просто бессмыслнно. Пускай от того что дотнет сожрал лишних 40 метров производительность увеличилась на 3%. Но она увеличилась. За что его можно ругать то? Память то попросу не используется.
PD>Вот и все. Советую все же прочитать книги по основам ОС, чтобы чепуху не писать.
С своей стороны советую убрать свой менторский тон и пойти учиться дальше. А то ты возмонил себе, что знашь все лучше других и поучашь всех на право и на лево. Не беспокойся за чужие знания. Я тоже читал и Рихтора и Русиновича. И не хуже тебя понимаю, что такое виртауальная память и с чем ее едят. Причем знаю это не по наслышке, так как сам сижу на NT со времен когдаона была еще 3.5. А вот ты судишь о том, в чем явно не разбирашся — о ЖЦ. И твои поучения выглядят просто смешно. Не признавать очевидные вещи конечно можно, но выглядишь приэтом глупо.
Сейчас не времена доса. Памяти сотни мег и нет смысла экономить ее в ущерб удобству и надежности. Темболее не стоит закатывать истерик плавно переходящих в компании по ловле ведьм тольно на основании того, что программа с новым гридом на машине с 2 гигами сожрала 60 мег. В конце концов это 3%. Зато на то чтобы сэмулировать возможности этого грид на ListView у тебя уйдет вся оставшаяся жизно. Разница между ними не меньше чем между Excel и SuperCalc.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>По большому счету, вообще — то, это их проблемы. Им же хуже, если на то пошло
Проблемы скорее у тех котому ты умудришся голову запудрить. Причем данный вопрос в общем-то мало интересен. Но вот твои посажи по абстракциям — это действительно супер. Ну, да будем надеяться, что твои ученики свою голову на плечах имеют.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Andy77, Вы писали:
ВВ>>Т.е. читать 10000 записей в грид — это нормально, а root of all evil — это дотнет? A>Бывает, что и нормально, нам ведь ничего не известно про его задачу.
Ну почему не известно. В этой ветке она довольно подробно обсуждалось.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>По большому счету, вообще — то, это их проблемы. Им же хуже, если на то пошло
VD>Проблемы скорее у тех котому ты умудришся голову запудрить. Причем данный вопрос в общем-то мало интересен. Но вот твои посажи по абстракциям — это действительно супер. Ну, да будем надеяться, что твои ученики свою голову на плечах имеют.
К сожалению, я сдела одну ошибку, в которой вынужден признаться. Ошибка в том, что я возобновил дискуссии с тобой. Дискутировать ты не умееешь, вместо этого переходишь на хамство. Поэтому вынужден отказаться впредь от каких-либо дискуссий с тобой.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>А чего вы хотите ?
ВВ>Т.е. читать 10000 записей в грид — это нормально,
С точки зрения интерфеса с пользователем — no comments. Я на эту тему не высказывался вообще и не хочу. Истина конкретна, а задачи я в деталях не знаю.
С точки зрения хранения в ОП 10000 элементов — вполне нормально. Грид, в конце концов, просто хранилище для них. В массиве миллион элементов — это нормально ? ИМХО вполне нормально.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>К сожалению, я сдела одну ошибку, в которой вынужден признаться. Ошибка в том, что я возобновил дискуссии с тобой. Дискутировать ты не умееешь, вместо этого переходишь на хамство. Поэтому вынужден отказаться впредь от каких-либо дискуссий с тобой.
Замечательно. За одно откажись от своих высказываний по дотнету, так как в основном они являются откровенной пропагандой ахинее. И именно они вызвают нездоровые дискуссии и общее раздражение.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Ты уперся в ворота и не хочешь признать банального факта. Дотнет отдает ресурсы системе если ей их нехватает. Он банально собирает мусор и уменьшает кучу. Посему смотреть на цифири получающиеся на машинах с 2 гигами ничем не занятыми просто бессмыслнно. Пускай от того что дотнет сожрал лишних 40 метров производительность увеличилась на 3%. Но она увеличилась. За что его можно ругать то? Память то попросу не используется.
VladD2,
не в коем случае не ставлю под сомнение твою информацию...
но у меня возник теоретический вопрос, с сугубо практическим интересом:
А как процесс Дотнета узнает что в системе ресурсов нехватает (память в данном случае)? Есть информация или хотя бы идеи по поводу как это происходит?
Здравствуйте, kedik, Вы писали:
K>А как процесс Дотнета узнает что в системе ресурсов нехватает (память в данном случае)? Есть информация или хотя бы идеи по поводу как это происходит?
Чтобы не повторяться просто процитирую свои слова из статьи GC в .NET
Есть три причины, вызывающих запуск процесса сборки мусора:
1. При очередном выделении памяти GC замечает, что превышен размер нулевого поколения.
2. Приложение самостоятельно вызывает метод GC.Collect().
3. Нехватка памяти в ОС.
Самой частой причиной является пункт 1. Пункт 3 обычно является большой редкостью. Ну а пункт 2 зависит от программиста, но обычно тоже крайне редок. Однако в Windows-приложениях пункт 2 может быть инициирован подсистемой учета ресурсов ОС (хендлов).
Пункты 3 приводит к сборке мусора второго поколения. GC.Collect() позволяет указать, какое поколение необходимо «подмести». А вот пункт 1 заставляет GC задуматься. Ход его мыслей приблизительно следующий: «Ага! Надо проверить лимит второго поколения. Если он превышен, то собрать его. Если нет, то проверить лимит первого поколения. Если он превышен, то собрать первое поколение. Иначе собрать только нулевое поколение».
Информация почерпнута из блогов и путем эксперементов. И судя по этой информации в каждом процессе есть некий код следящий за состоянием ОС. К сожалению он не так разумен как хотелось бы, что иногда приводит к печальным последствиям. Например, в той же статье описан вот такой случай:
Нехватка памяти
Всем известно, что при нехватке физической памяти приложение начинает сбрасывать в файл подкачки (swap file) редко используемые части занятой оперативной памяти.
Происходит это, когда приложению нужно памяти больше, чем на данный момент свободно в системе. При этом ОС в первую очередь пытается сбросить свои внутренние буферы (например, кэш файловой системы). Если этой памяти не хватает, начинается «свопинг».
Если память, сброшенная в файл подкачки, действительно используется редко, то замедление получается не такое уж большое. Ведь обращение к памяти относительно локализовано, и swapping производится не часто. Однако файл подкачки – это обычный дисковый файл, а скорость работы дисковой подсистемы в тысячи раз медленнее, чем скорость оперативной памяти.
Чтобы вы могли оценить затраты, связанные с подкачкой, я создал синтетический тест, в котором сначала занимается гандикап – огромное количество памяти (под 10 миллионов объектов, каждый из которых содержит массив длиной от 1 до 128 байт), а затем, не освобождая эту память (храня ссылки на объекты в массиве), производится эмуляция стандартной работы приложения (занимаются и освобождаются объекты). В результате создается ситуация, при которой во время эмуляции стандартной работы объем занятой оперативной памяти превышает объем физической памяти, доступной в этот момент в системе (в системе было установлен гигабайт оперативной памяти и параллельно было замещено немало приложений, общий объем занимаемой ими памяти приблизительно был равен 400 мегабайтам).
Основной тест написан на C# и, естественно, занимает память в управляемой куче. Также я создал аналогичные тесты на C++, занимающие память в обычной куче Windows, и модификацию C++-теста, занимающую память с использованием библиотеки QuickHeap (http://gzip.rsdn.ru/article/cpp/QuickHeap.xml
), ускоряющей работу с памятью в C++-приложениях за счет использования более быстрого, но более прожорливого алгоритма.
Все три теста выполняются сначала с миллионом объектов в качестве гандикапа, а затем – с 10 миллионами. В первом случае объем занимаемой памяти, хотя и велик, но не превышает объема свободной памяти в системе, в которой производилось тестирование. Во втором случае объем гандикапа приблизительно равен объему оперативной памяти в системе. Расчет делается на то, что в обоих случаях после занятия гандикапа приложение не трогает принадлежащие к нему объекты. Вместо этого оно начинает интенсивно выделять большое количество объектов, не удерживая ссылки на них в управляемом тесте, и освобождая объекты в неуправляемых тестах. Таким образом, в основной части теста работа одновременно ведется с небольшим количеством объектов и, соответственно, памяти.
Ниже приведены результаты C++-теста, использующего стандартную кучу Windows:
e:\MyProjects\Tests\Perf\CppNew2\release>CppNew2.exe
Timestamp: 4.238123 sec.
ObjCount=11001000
e:\MyProjects\Tests\Perf\CppNew2\release>CppNew2.exe
Timestamp: 37.864712 sec.
ObjCount=20001000
Значение ObjCount – количество объектов, создаваемых при работе теста, включая как гандикап, так и рабочие объекты. Как видите, второй тест сильно отстал от первого, но отставание все же уложилось в разумные рамки (порядка 10 раз).
Если последить за картиной занятия памяти, то наблюдается следующее. При запуске с миллионом объектов объем занятой виртуальной памяти быстро поднималась примерно до 100 мегабайт и держалась на этом уровне до конца теста. Объем workset-а так же быстро достигал 100 мегабайт и так же неизменно держался до окончания теста.
При запуске теста с десятью миллионами объектов картина в корне отличалась. Сначала и workset и объем виртуальной памяти быстро достигали отметки в 400+ мегабайт. Далее объем виртуальной памяти продолжал постепенно (уже не так быстро) расти, а объем workset-а начал падать и стабилизировался на отметке 10-12 мегабайт (это объем занимаемый объектами, одновременно живущими на стадии «эмуляции обычной работы приложения»).
Совершенно ясно, что если бы приложение начало во время работы обращаться к предварительно занятым объектам (тем самым 10 миллионам), то все было бы значительно печальнее, так как при этом начался бы постоянный свопинг, и основное время тратилось бы на работу с винчестером.
Ради удовлетворения любопытства я повторил тот же тест с использованием своей библиотеки QuickHeap:
e:\MyProjects\Tests\Perf\CppNew2\release>CppNew2.exe
Timestamp: 2.193539 sec.
ObjCount=11001000
e:\MyProjects\Tests\Perf\CppNew2\release>CppNew2.exe
Timestamp: 14.508904 sec.
ObjCount=20001000
Забавно, что даже в условиях свопинга она показала намного лучший результат, чем стандартная куча Windows. Забавно это потому, что QuickHeap достигает своего быстродействия за счет перерасхода памяти. Но еще забавнее было наблюдать за картиной выделения памяти.
При запуске с миллионом объектов объем занятой виртуальной памяти скакнул к ~130 мегабайтам, что существенно выше, нежели в тесте с кучей Windows, но объем workset-а вырос всего ~96 мегабайт! Таким образом, получается, что QuickHeap хотя и тратит больше памяти, но обеспечивает лучшую локализацию данных (по крайней мере, при первом обращении).
Запуск теста с 10 миллионами предварительно занятых объектов привел к очень быстрому занятию порядка гигабайта виртуальной памяти и довольно быстрому увеличению этого объема до 1.3 гигабайта (напомню, что куча Windows постепенно росла вплоть до отметки в 1 гигабайт). Затем объем виртуальной памяти стабилизировался. Workset рос плавно (но быстро) до отметки в 400 мегабайт, после чего столь же плавно (но столь же быстро) спустился до отметки 14-9 мегабайт.
Большую скорость QuickHeap я в данном случае могу объяснить именно лучшей локальностью данных, что вызвало меньшее количество циклов свопинга. Так что хотя общее количество виртуальной занятой памяти было больше, но обращений к диску (как не странно) оказалось значительно меньше.
Ну, а что же GC? Для GC подобный тест оказался самым плохим паттерном использования. Вот его результаты:
e:\MyProjects\Tests\Perf\GC2\GC2\bin\Release>GC2.exe
00:00:02.1843580
Количество созданных объектов: 11001000
Количество сборок мусора поколения 0: 590
Количество сборок мусора поколения 1: 154
Количество сборок мусора поколения 2: 7
e:\MyProjects\Tests\Perf\GC2\GC2\bin\Release>GC2.exe
00:20:47.7608425
Количество созданных объектов: 20001000
Количество сборок мусора поколения 0: 769
Количество сборок мусора поколения 1: 273
Количество сборок мусора поколения 2: 27
Да-да! Две секунды на тест с миллионом объектов и более 20 минут (!) на тест с 10 миллионами!!! Почему такая разница? Думаю, самые прозорливые уже догадались, почему это так (я запустил параллельно архиватор! – шутка). Секрет заключается в том, что при сборке мусора второго поколения GC «дотрагивается» до всех объектов кучи. А это приводит к тому, что Windows поднимает страницы виртуальной памяти, на которых размещены объекты, с диска в физическую память. Это подтверждает и картина использования памяти. Объем виртуальной памяти начинает расти и очень быстро доходит до ~900 мегабайт. Далее он изменяется очень незначительно. А вот workset начинает постоянно расти и, достигнув отметки в 900 мегабайт, сбрасывается до ~400-500 мегабайт. Далее он снова начинает расти до 900 и снова сбрасывается. Так продолжается на протяжении двадцати минут.
В принципе, это не нормально для сборщика мусора, основанного на поколениях, так как он должен был бы пропихнуть неиспользуемые в данный момент объекты во второе поколение и забыть про них. Но почему-то наличие объектов во втором поколении постоянно подталкивает GC к сборке мусора во втором поколении. Причем делает это он тем чаще, чем больше объем второго поколения. Налицо эвристика. В чем она заключается, не знаю, но учитывать это придется. Скорее всего, столь неразумное поведение GC связано с тем, что сборка мусора второго поколения запускается при нехватке памяти в системе. Получается замкнутый круг: нехватка памяти в системе провоцирует сборку мусора второго поколения, а сборка мусора второго поколения провоцирует поднятие страниц в память. На самом деле, это просчет программистов, писавших GC, так как по-хорошему реагировать нужно было бы на нехватку физической памяти.
Какой из этого можно сделать вывод? GC хорошо ведет себя, только если имеет достаточно свободной памяти. Если workset приложения начинает дергаться, то подбираясь к объему виртуальной памяти, то сбрасываясь, знайте, что, скорее всего, вы заняли слишком много лишней памяти. Большое количество сборок второго поколения также говорит о том, что профиль вашего приложения плохо совместим с эвристиками GC.
Что же делать? Пересмотреть политику создания и удержания в живых объектов. Возможно, отказаться от кэша или подгружать малозначимые данные по требованию. Еще один простой и дешевый совет – купить лишний гигабайт памяти.
Но описанный тест — это чистой воды синтетика которой в жизни не встретишь. Обычно процессы занимают памяти меньше чем физически есть в системе и работают относительно по очереди. К тому же сборка мусора в рельных приложениях освобождает не мало памяти, что устраняет проблемы. Другими словами алгоритм в обычных условиях справляется со своими обязанностями неплохо.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.