Войти в систему

Home
    - Создать дневник
    - Написать в дневник
       - Подробный режим

LJ.Rossia.org
    - Новости сайта
    - Общие настройки
    - Sitemap
    - Оплата
    - ljr-fif

Редактировать...
    - Настройки
    - Список друзей
    - Дневник
    - Картинки
    - Пароль
    - Вид дневника

Сообщества

Настроить S2

Помощь
    - Забыли пароль?
    - FAQ
    - Тех. поддержка



Пишет dibr ([info]dibr)
@ 2010-08-22 18:13:00


Previous Entry  Add to memories!  Tell a Friend!  Next Entry
За программизм
     Сразу предупрежу - я не программист, поэтому могу быть очень сильно не в курсе текущего состояния дел. Но есть мысли, и я их думаю.

     Бывает, что программы "мучительно долго закрываются": нажал ты крестик, а она ещё минут несколько шуршит диском и грузит процессор, прежде чем покинуть список процессов. Говорят, при этом аккуратный рантайм объектно-ориентированного языка аккуратно и поочерёдно уничтожает созданные за время работы программы объекты. Аккуратно, методично, с соблюдением всех требуемых в языке формальностей, потому долго. Вот я и задумался на тему оптимизации уничтожения объектов. Создавать-то мы все умеем, а вот уничтожать с наименьшей нагрузкой на процессор?...

        1) Ведь можно же, чтобы объект при уничтожении "грохался молча", без каких-то сопутствующих действий кроме собственно освобождения памяти? Мне казалось, что оно по умолчанию так и есть (если для уничтожения не прописано явно каких-то действий), но что-то берут меня сомнения: нет ли в сегодняшних языках/рантаймах каких-то внутренних навесок "обязательных к исполнению при удалении объекта"? И если есть - почему бы не сделать для объекта флаг "можно грохать молча".
        1.1) ...ну, или "грохать молча" хотя бы при завершении работы программы (при установленном флаге, ессно - чтобы старые программы не словили каких-нибудь спецэффектов). Программа завершается, ей больше не нужны вообще никакие объекты, а нужные именно для завершения действия она и сама сделает (если будет знать, что объекты грохаются молча).
        2) "Куча в куче" или "объект типа куча". Создаем "новую кучу" (саму являющуюуся например объектом), внутри этой кучи создаём нужные нам объекты. Когда надобность в объектах отпала - грохаем кучу целиком, не заглядывая внутрь (и экономя тем самым работу по уничтожению каждого объектика по отдельности). Удобно понятно для чего - насоздавали для временных нужд много временных объектиков, потом грохнули все разом.
        3) "Объект можно грохать, если нужно - восстановим". При работе программ создаётся множество "временных" объектов, в принципе нужных, но не необходимых - какие-нибудь индексы, распакованный в память для лучшей скорости отображения jpeg, и подобные вещи, которые вроде и удалять жалко (а главное - непонятно когда), и место занимают, и пересоздаются в случае чего легко. Ввести для таких объектов флаг "можно грохать, для восстановления вызвать вот это". Если менеджер кучи обнаруживает нехватку памяти, и при этом есть давно не используемые объекты с таким флагом - их можно грохнуть, оставив от них только ссылку на процедуру пересоздания объекта. Если программа обращается к такому объекту - менеджер кучи зовёт процедуру пересоздания объекта, и только потом допускает программу до объекта.
        3.1) ...как вариант - грохать такой объект без сохранения чего либо, при обращении к объекту программы - сообщать программе что объект мы грохнули, пусть выкручивается как хочет (обходится без него, или создаёт заново).
        3.2) ...кстати, для dz phantom, с её (его?) персистентностью, эта проблема (замусоривание кучи объектами, которые не особо-то и нужны, но нет формальных причин для их удаления) может оказаться актуальной.

     Интересно, много я сегодня велосипедов наизобретал?...


(Добавить комментарий)


[info]hml@lj
2010-08-22 14:21 (ссылка)
Часть из этого реализована в Objective-C (и используется при программировании под iPhone OS)

(Ответить) (Ветвь дискуссии)


[info]themoriarty@lj
2010-08-22 14:23 (ссылка)
А что, например? И именно в objective-c или iOS SDK?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]hml@lj
2010-08-22 17:54 (ссылка)
см. Autorelease Pool. Кроме того, там сообщение dealloc может не посылаться объектам вовсе при терминации программы (просто все прибивается скопом), а еще, ЕМНИП, есть лимит времени на завершение, после которого программа прибивается насильно.

(Ответить) (Уровень выше)


[info]themoriarty@lj
2010-08-22 14:21 (ссылка)
Все это делать можно, но есть три нюанса:
1. о скорости завершения программы никто не думает :).
2. не все так просто - если у нас refcounted-объекты, и для завершения программы достаточно явно удалить один-единственный (остальные размотаются по цепочке), этот вариант не сработает.
3. при выходе из программы бывает полезно за собой почистить - удалить временные каталоги и файлы. На это точно нельзя забивать.

П. 2, кстати, используется довольно часто - кастомные memory-manager'ы используют, например, для быстрой аллокации кучи одинаковых объектов (http://goog-perftools.sourceforge.net/doc/tcmalloc.html читать про small object allocation и large object allocation)

(Ответить) (Ветвь дискуссии)


[info]dibr@lj
2010-08-22 16:05 (ссылка)
1. А когда это становится проблемой - можно выходить в обход рантайма, системным вызовом :-)
2. Если программа написана именно так - то да, существенно ничего не изменится. Но тогда давайте сделаем "кучу в куче", или хотя бы банально две кучи - "расходную" (грохается молча со всем содержимым), и "рабочую" (которую грохать нельзя). При выходе аккуратно "разматываем" удаление объектов из "рабочей" кучи (и они выполняют необходимую чистку), а расходную кучу грохаем просто так.
3. Каталог можно и ручками удалить. В том смысле, что если программист озаботился скоростью завершения работы, и помнит какие каталоги создаются и подлежат удалению - то каталог можно и явным образом удалить, без объектов. А объекты просто грохнуть. А временные файлы сами удалятся, если сразу сделать их delete-on-close :-)

> memory-manager'ы используют, например, для быстрой аллокации кучи одинаковых объектов

О. Теперь это более подконтрольным сделать, и использовать для группировки и массового удаления "за один взмах".

(Ответить) (Уровень выше)


[info]ilya_314@lj
2010-08-22 17:12 (ссылка)
Об этом должны думать те, кто реально с этим сталкивается. Когда opera 9 при закрытии 50 табов задумывалась на 30 сек, это несколько напрягало, потом вылечили.

Другой пример vs2005, солюшн из 30 проектов, работаю целый день - закрываться может минуту (!), vs2008 уже намного лучше.

В обоих случаях так или иначе что-то улучшали.

(Ответить) (Уровень выше)


[info]azgar@lj
2010-08-23 16:26 (ссылка)
Да, при создании объекта трудно гарантировать, что тот не создаст какой-то другой объект, который, в свою очередь, залочит внешний ресурс.
А у нас ещё и полиморфизм и динамически линкуемый код. То есть мы вообще не можем предсказать, что там будет, когда кто-то подсунет другую версию библиотеки.
То есть деструкторы, таки, должны быть вызваны по всему множеству освобождаемых объектов. А для этого, как минимум, надо вытащить объект из свопа, что и занимает обычно неприличное время.

(Ответить) (Уровень выше)


[info]pozitronik@lj
2010-08-22 15:17 (ссылка)
Нажатие на "крестик" не означает завершение процесса. Это всего лишь событие, которое может к нему вести.
Потому в 99% случаев тормоза и шуршание диском не из-за того, что программа уничтожает объекты (хотя и это может быть, например, из-за свопирования), а из-за того, что при завершении (но до уничтожения) она ещё что-то должна сделать по замыслу программиста. Провести сохранение состояния на будущее, почистить кеш, помолиться Аллаху, ну и т.д.
Плюс операционная система сама должна чистить память, которую занимала программа, если та была завершена без полагающихся процедур, так что о этих процедурах действительно мало кто заботится.

(Ответить) (Ветвь дискуссии)


[info]dibr@lj
2010-08-22 16:11 (ссылка)
Ну, что-то она перед выходом делает, это я согласен (хотя то, что мне известно - то же сохранение состояния - вроде бы должно идти сильно быстрее). А кеш-то какой чистить и зачем? В памяти - сам умрёт, на диске (браузерный) - можно (и нужно) чистить в любой момент, не только при выходе.

> Плюс операционная система сама должна чистить память, которую занимала программа, если та была завершена без полагающихся процедур, так что о этих процедурах действительно мало кто заботится.

Об этих процедурах рантайм заботится. Мало кто из современных программистов по завершению работы программы "выдёргивает вилку" соответствующим системным вызовом (тогда действительно, все объекты просто исчезают вместе с процессом), большинство раз уж начали работу программы через создание объектов - завершают через уничтожение. И вот это вот уничтожение может занять некторое время...

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]pozitronik@lj
2010-08-22 16:30 (ссылка)
>А кеш-то какой чистить и зачем?
Это - как пример операции, а не что-то обязательное. Opera, например, при некоторых настройках именно так и поступает - чистит кеш после закрытия, при этом долго вися в списке процессов.
>можно (и нужно) чистить в любой момент
И зачем он тогда нужен?
>Об этих процедурах рантайм заботится.
Само собой. Но если мы сделаем программе kill -9, участок памяти, который она занимала, не останется занят до перезагрузки, а спокойно освободится силами ОС.

В общем, меня смущает то, что по твоему описанию умирающая программа шуршит диском. Если уничтожаемые объекты в памяти - как бы медленно они не стирались, диск шуршать не будет. Ежели память (в которой объекты) засвопирована - может шуршать, но тут уж имеем что имеем.
Во всех остальных случаях, как мне кажется - прога таки что-то ещё делает.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]sergey_cheban@lj
2010-08-23 06:14 (ссылка)
> В общем, меня смущает то, что по твоему описанию умирающая программа шуршит диском.
А вот, кстати, ещё один повод для шуршания. У нас ведь программа - это memory mapped file. И часть страниц этого файла, нужная только при завершении, может быть попросту не подгружена в память.

А вообще - для каждой программы будет свой ответ. Это может быть и "нет никаких тормозов", и "ошибка реализации", и "а вот теперь-то мы и сделаем commit".

(Ответить) (Уровень выше)


[info]ilya_314@lj
2010-08-22 17:15 (ссылка)
По поводу уничтожения объектов могу утверждать, что может быть намного хуже. Сталкивался на собственном опыте, delete поверьте во много раз медленнее new, думаю на порядок. При аллокации сотен тысяч объектов уже будет эффект весьма заметный. Это обычно сочетается в реальных условиях крупных приложений с другими усугубляющимися действиями, типа подкачки.

Вот я уже привел два примера - как закрывалась opera 9 и vs2005 на больших солюшнах - они ничего не должны были никуда записывать, просто закрыться.

(Ответить) (Уровень выше)


[info]ilya_314@lj
2010-08-22 17:16 (ссылка)
Про очистку памяти операционной системой - это из разряда пожеланий или утверждение?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]dibr@lj
2010-08-22 17:35 (ссылка)
Вроде бы, утверждение.

Но что мы называем очисткой? Освобождение - да, конечно, после смерти приложения все занимаемые им ресурсы освобождаются (помечаются как свободные). Память с точки зрения ОС - просто кусок адресного пространства, "объектов" там нет, поэтому проблем тут нет - приложение умерло, про объекты никто даже не вспомнил, память вернулась системе как свободная.

Если очистка - это зануление, то насколько я понимаю, сразу после освобождения памяти зануление не производится (зачем тратить ресурсы на зануление, если эта область может быть перезаписана системой, например при чтении в неё чего-нибудь с устройства или копировании туда каких-то данных). При этом выделяемая приложению новая память гарантированно зануляется (требование безопасности, чтобы тебе не попали случайно чужие пароли, да и постабильнее оно так), а при простое системы пресловутый "system idle process" в частности фоном зануляет неиспользованные страницы, чтобы не занулять их непосредственно перед выделением.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ilya_314@lj
2010-08-22 17:48 (ссылка)
В системе действительно есть малоприоритетный поток, который освободившиеся страницы заполняет нулями, это сделано для увеличения вероятности стабильного падения приложений. Никакого обнуления при выделении и освобождении принудительно не производится, если страница успела очистится, то действительно придет нулевая, но гарантий нет.

В программах написанных например на C/C++ все статические данные гарантированно инициализированы нулями, это просто правило языка, эти нули хранятся в exe/dll, в run-time ничего такого нет, поэтому неинициализированная память одна из частых причин трудоемкой отладки такого кода. В debug конфигурациях инчае работает CRT - там происходит заполнение "CD" всей аллоцируемой памяти.

В .net все объекты по определению подлежат инициализации, поэтому такой проблемы просто не стоит.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]dibr@lj
2010-08-22 17:56 (ссылка)
А что обнуления при выделении (системой) не происходит - это точно?

Просто мне глючится что я где-то слышал, что система перед выдачей гарантированно обнуляет память, именно для безопасности (пароли, "личные данные"). Но утверждать что это действительно так не могу, поскольку именно что что-то где-то когда-то слышал.

А рантаймы действиткльно могут обнулять или не обнулять сами, с разной для этого мотивацией.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ilya_314@lj
2010-08-22 18:29 (ссылка)
Да, я тут не прав оказался. Обнуление идет в фоне, но когда идет запрос на новую страницу, то берем из очереди обнуленных, если пусто то сразу обнуляем. Действительно из-за безопасности.

А что касается debug - то там заполнение константой при каждом выделении блока, даже поверх уже выделенного, поэтому воспроизводимость ошибок выше.

(Ответить) (Уровень выше)


[info]hml@lj
2010-08-22 18:00 (ссылка)
Зануление нужно для безопасности, чтобы никто не мог просканить освобожденную память процесса, где могут быть нешифрованные пароли, номера кредиток и т.п. Помню, в первых ядрах линукса была забавная дырка: можно было выделить себе 1 байт и система честно этот байт обнуляла. А остальные 4095 байт страницы оставались необнуленными и, выделив себе 1 байт, а потом увеличивая лимит по 4096 байт за раз, можно было поискать всякие интересности :)

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ilya_314@lj
2010-08-22 18:29 (ссылка)
Да, я тут не прав оказался. Обнуление идет в фоне, но когда идет запрос на новую страницу, то берем из очереди обнуленных, если пусто то сразу обнуляем. Действительно из-за безопасности.

(Ответить) (Уровень выше)


[info]ilya_314@lj
2010-08-22 16:53 (ссылка)

При закрытии приложений большая часть времени уходит на освобождение памяти, это факт.

В win32 собтсвенно куча это объект, но как ты понимаешь, для того чтобы с этим работать надо везде переопределять аллокаторы, а с этим как-то не очень сложилось в языках типа c/c++, т.е. нужно себя постоянно контроллировать. Тогда удаление будет выглядеть так - послать всем уведомление - завершить работу, тогда все закрывают файлы, сбрасывают буферы, потом просто всю кучу убиваем. Повторю - проблема в отсутствии удобных механизмов.

Кстати в .net есть специальный API для управления garbage collector, но не уверен, что он может помочь в решении этой задачи там.

(Ответить)


[info]demon_nn@lj
2010-08-30 09:28 (ссылка)
1. Язык программирования не знает можно ли "грохать" объект или же его обязательно нужно чистить. Как результат - чистит все.

2. Оптимизация и ускорения процесса завершения работы программы - это работа, которая 99.9% пользователям будет незаметна. По этому такую работу и не делают, достаточно по быстрому убрать с экрана окно приложения и вся вина в "тормозах" ложиться на ОС.

Велосипед ты изобрел хороший, годный.
Много чего реализовано в движках 3Д графики. Там очень часто приходится конструировать довольно сложные структуры данных и быстренько "грохать" их после отрисовывания следующего кадра (подозреваю, что и в обработке видео потоков такая-же фигня). При всем при этом есть жесткие лимиты времени, т.к. ФПС - это святое и то, что сразу бросается в глаза. То, что ты напридумывал - фигня, по сравнению с тем, что там насоздавали. Простой пример - менеджер памяти: Память выделяется только через него. Он сам "берет" ее огромными кусками (дабы не тормозить с малоками), которые объединяет в список. От этих кусков он, по мере надобности, выделяет нужное просто двигая указатель на свободный кусок. "Освобождения" объектов не существует в принципе. После отрисовки кадра весь менеджер объявляется пустым и указатель на свободную область возвращается в исходное положение.

(Ответить) (Ветвь дискуссии)


[info]ilya_314@lj
2010-08-30 14:51 (ссылка)
Ускорение закрытия работы программы не заметно для пользователя пока его это не начинает напрягать. Я уже приводил примеры - закрытие Visual Studio после долгой работы с проектом, закрытие opera с большим числом закладок.

То, что ты называешь менеджер памяти, это куча о которой говориться в пункте 2 поста dibr-a, т.е. эта идея тут озвучена.

Я это пишу к тому, что несколько не понял почему предложенное фигня или я не совсем понял посыл?

(Ответить) (Уровень выше)


[info]dibr@lj
2010-08-30 15:19 (ссылка)
2. С оптимизацией грохания объектов в десктопных приложениях вопрос тёмный - что там творится при закрытии действительно мало кого волнует, но объекты грохают ведь и по ходу дела, когда они становятся не нужны, и это можно и оптимизировать, той же "кучей в куче" (гроханием пачки объектов). С третьей стороны - гроханием сейчас всё чаще занимается не программист, а GC, а GC такую оптимизацию вряд ли сумеет - даже если связать объекты в "кластер", не факт что станет лучше...

1. Так флажки же, или "хинты", то есть свойства, прописывающие "правила грохания"! Сейчас таких хинтов нет, и рантайм удаляет объекты только "по правильному" (а GC не удаляет объект, пока он хоть кому-нибудь "нужен"). Пусть у объекта будет опциональное свойство, разрешающее GC грохать его молча, если GC решит, что сейчас есть некоторая нехватка памяти (или что этот объект подозрительно долго не использовался). Или, скажем, разрешит грохать объект "с уведомлением приложения" (а приложение при этом, например, грохнет ещё несколько связанных объектов). Естественно, если приложение не в курсе ни про какие флажки - всё будет по старому, зато если в курсе - программисты смогут создавать и использовать развесистые временные структуры, не заботясь о том, когда именно их удалять - их удалит менеджер памяти, тогда, когда памяти станет мало или когда структуру долго никто не использует.

3. В "критичных ко времени" приложениях, в отличие от неторопливых "офисных", действительно много чего может быть "на вид кривого, зато очень быстрого и почти всегда работающего" :-) Но в критичных ко времени участках кода как-то и "объекты" в их классическом виде не очень приживаются, работают действительно с кусками памяти, массивами, указателями, и прочей атрибутикой программирования образца конца XX века. Я-то про более строгие системы/программы, и цель - не предельное быстродействие ценой более "низкоуровневой" работы с памятью, а скорее уменьшение замусоривания памяти ненужными долгоживущими объектами, и может быть некоторое ускорение...

(Ответить) (Уровень выше)


[info]slowkukuing@lj
2010-09-11 23:19 (ссылка)
1. так в джаве сделано (поскольку нет деструкторов). Если нужно что-то таки "деструктить", то объект должен реализовать finalize
1.1. опять же - в джаве exit() отрабатывается "мгновенно". Т.е. считается что все "внешние" ресурсы "аллокируются" через jvm и, соотв., jvm по завершению их вернёт в систему (да ещё и система, на самом деле, "отбирает" ресурсы у убиваемого процесса)

2. видел такое в Obj-C, но ни разу не юзал и не видел, чтобы кто-то юзал.

3. в джаве - Weak/Soft Reference и иже с ними. юзал.
не оч. удобно, поскольку для полноты идеи нужно закрывать доступ непосредственно к объекту через прокси, а прокси в джаве строятся только от интерфейсов (уррроды!)

-+-
реально, как уже ниже (т.е. выше) упомянули, "тормоза" проистекают не от "деструкторов" как таковых, а в силу неких "завершающих" процедур - временные файлы почикать, то-сё в реестре записать/постирать... некоторые проги вообще для своей работы запускают сервисы, которые по выходу пытаются положить и ты.ды.


(Ответить)