Пул из heap (куч). Имеет ли это смысл?
От: TheBeginner  
Дата: 11.06.23 04:37
Оценка:
Например есть приложение, создающее (HeapCreate) от нескольких десятков до 200 или более куч для размещения в каждой куче объектов своего размера с возможностью освободить их все разом (HeapDestroy). Это одна из задач для чего собственно собственные кучи и создаются.
Имеет ли смысл объединить их во что-то вроде пула таким образом, чтобы при ошибке создания новой кучи (HeapCreate) или выделения памяти (HeapAlloc, HeapReAlloc), память выделять из другой кучи пула. Если пока не рассматривать вопросы вроде хранения указателей на блоки, выделенные не своей куче для возможности удаления кучи из пула, а определится — имеет это смысл и нет?
Насколько помню — HeapCreate резервирует 1 Мб адресного процесса. Вызов HeapCreate в цикле на 32 битном процессе создает 20000 куч.
Насколько часто HeapCreate/HeapAlloc заканчиваются неудачно? Это связано только с исчерпанием адресного пространства, недостатком физической памяти, порчей внутренних структур данных кучи или еще что-то?
Re: Пул из heap (куч). Имеет ли это смысл?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 11.06.23 07:02
Оценка: 1 (1)
Здравствуйте, TheBeginner, Вы писали:

TB>Имеет ли смысл объединить их во что-то вроде пула


Смысл может появиться только после анализа производительности.
Re[2]: Пул из heap (куч). Имеет ли это смысл?
От: TheBeginner  
Дата: 11.06.23 07:43
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Смысл может появиться только после анализа производительности.


Тут вопрос не в производительности. Возможно пул не совсем удачное определение для этого Вопрос в том, что имея в какой то момент 200+ куч имеет ли смысл подмены одной кучи другой при неудачном вызове HeapCreate или HeapAlloc.
Re[3]: Пул из heap (куч). Имеет ли это смысл?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 11.06.23 09:51
Оценка:
Здравствуйте, TheBeginner, Вы писали:

TB>имея в какой то момент 200+ куч имеет ли смысл подмены одной кучи другой при неудачном вызове HeapCreate или HeapAlloc.


Если такая подмена будет реализована, то какой останется смысл в наличии 200+ куч?
Re[4]: Пул из heap (куч). Имеет ли это смысл?
От: TheBeginner  
Дата: 11.06.23 10:32
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Если такая подмена будет реализована, то какой останется смысл в наличии 200+ куч?


OK. Попробую объяснить еще раз. У нас есть 200 куч. Мы хотим создать 201, но HeapCreate возвращает в этот раз NULL. Или мы хотим сделать HeapAlloc в одной из существующих куч и получаем NULL. Но у нас уже есть 200 куч и можно попробовать выделить память в одной из существующих куч. Так как мы пользуемся возможностью освободить память одним махом в куче с помощью HeapDestroy нам необходимо хранить указатели (в виде бинарного дерева например) на блоки памяти выделенные не в своей куче чтобы кучу можно было удалить только если она не задействована для хранения других данных. Но это уже детали. Главный вопрос — если вызов HeapCreate или HeapAlloc вернул NULL, означает ли это, что попытка выделения памяти в другой существующей куче также скорее всего будет неудачной, так как проблема с HeapCreate или HeapAlloc чаще всего из-за исчерпания адресного пространства или физической памяти и не стоит пытаться выделять память в другой куче — результат также скорее всего в таком случае будет NULL.
Re: Пул из heap (куч). Имеет ли это смысл?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 11.06.23 12:02
Оценка:
Здравствуйте, TheBeginner, Вы писали:

TB>Насколько помню — HeapCreate резервирует 1 Мб адресного процесса. Вызов HeapCreate в цикле на 32 битном процессе создает 20000 куч.

TB>Насколько часто HeapCreate/HeapAlloc заканчиваются неудачно? Это связано только с исчерпанием адресного пространства, недостатком физической памяти, порчей внутренних структур данных кучи или еще что-то?

20К куч по метру? В 4 гига адресного пространства только 4000 мегабайт влазит, по моей отсталой арифметике
Маньяк Робокряк колесит по городу
Re[5]: Пул из heap (куч). Имеет ли это смысл?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 11.06.23 12:05
Оценка:
Здравствуйте, TheBeginner, Вы писали:

TB>У нас есть 200 куч.


Если "вопрос не в производительности", то зачем нужны отдельные кучи? Их как раз и создают, чтобы ограничить размеры списков свободных блоков и фрагментацию общей кучи.

TB>Мы хотим создать 201, но HeapCreate возвращает в этот раз NULL.


Это значит, что закончилась либо реальная память (или квота на нее), либо адресное пространство.

TB>Или мы хотим сделать HeapAlloc в одной из существующих куч и получаем NULL.


Если при создании кучи не был указан ее максимальный размер, куча будет свободно расти, пока есть, куда (АП и реальная память). HeapAlloc вернет нуль лишь тогда, когда кончилось либо АП, либо реальная память.

TB>Но у нас уже есть 200 куч и можно попробовать выделить память в одной из существующих куч.


Это может понадобиться лишь в том случае, когда при создании куч задавался максимальный размер, но какой в этом смысл?

TB>мы пользуемся возможностью освободить память одним махом в куче с помощью HeapDestroy


А зачем? Такое полезно, если поделка наколенная, и лень возиться с учетом блоков, или когда блоков очень много, и поштучное освобождение настолько тормозит, что приходится жертвовать надежностью.

TB>если вызов HeapCreate или HeapAlloc вернул NULL, означает ли это, что попытка выделения памяти в другой существующей куче также скорее всего будет неудачной


В общем случае — нет. Если для кучи задается максимальный размер, то резервируется диапазон АП, независимый от других куч. Если нет, то каждая куча резервирует новые диапазоны по мере исчерпания доступных.
Re[2]: Пул из heap (куч). Имеет ли это смысл?
От: TheBeginner  
Дата: 11.06.23 12:37
Оценка:
Здравствуйте, Marty, Вы писали:

M>20К куч по метру? В 4 гига адресного пространства только 4000 мегабайт влазит, по моей отсталой арифметике


Кстати да
Но в цикле успешно получил 23681 для 32 разрядного процесса. А по книжке Рихтера специально посмотрел сейчас — резервирует 1 мб адресного пространства каждая куча. Видимо уже не так.
Re[3]: Пул из heap (куч). Имеет ли это смысл?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 11.06.23 12:40
Оценка: +1
Здравствуйте, TheBeginner, Вы писали:

M>>20К куч по метру? В 4 гига адресного пространства только 4000 мегабайт влазит, по моей отсталой арифметике


TB>Кстати да

TB>Но в цикле успешно получил 23681 для 32 разрядного процесса. А по книжке Рихтера специально посмотрел сейчас — резервирует 1 мб адресного пространства каждая куча. Видимо уже не так.


Или при создании кучи ничего не резервируется, а резервируется после первой аллокации в куче, например. Попробуй в том же цикле в каждой новой куче выделять память
Маньяк Робокряк колесит по городу
Re[6]: Пул из heap (куч). Имеет ли это смысл?
От: TheBeginner  
Дата: 11.06.23 13:06
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Если "вопрос не в производительности", то зачем нужны отдельные кучи? Их как раз и создают, чтобы ограничить размеры списков свободных блоков и фрагментацию общей кучи.


Каждая куча под блоки одного размера. Как одна из задач чтобы избежать фрагментации при выделении/освобождении блоков и создаем много куч.

TB>>мы пользуемся возможностью освободить память одним махом в куче с помощью HeapDestroy


ЕМ>А зачем? Такое полезно, если поделка наколенная, и лень возиться с учетом блоков, или когда блоков очень много, и поштучное освобождение настолько тормозит, что приходится жертвовать надежностью.


От специфики задачи конечно зависит, но имхо иногда вещь полезная.

TB>>если вызов HeapCreate или HeapAlloc вернул NULL, означает ли это, что попытка выделения памяти в другой существующей куче также скорее всего будет неудачной


Забыл упомянуть — кучи растущие, для которых не задан максимальный размер. Если еще упростить вопрос — Если создаем множество растущих куч и один из вызовов HeapCreate или HeapAlloc оказался неудачным, имеет ли писать код, который будет пытаться выделять память в "чужой" куче. HeapCreate/HeapAlloc в большинстве случаев вернет NULL при исчерпании адресного пространства или недостатке физической памяти так что попытка выделения памяти в чужой куче также скорее всего вернет NULL.
Re[7]: Пул из heap (куч). Имеет ли это смысл?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 11.06.23 14:18
Оценка:
Здравствуйте, TheBeginner, Вы писали:

TB>имеет ли писать код, который будет пытаться выделять память в "чужой" куче.


Если под смыслом понимается "будем счастливы, если удастся создать хоть один блок", то имеет. Если планируется продолжать сколько-нибудь длительную работу без освобождения памяти, то вряд ли.

TB>HeapCreate/HeapAlloc в большинстве случаев вернет NULL


Вы понимаете, что они вернут нуль в достаточно разных условиях?

TB>попытка выделения памяти в чужой куче также скорее всего вернет NULL.


Если блок достаточно большой, то эти условия практически совпадают. Если же запрашивать блоки в десятки байт, то для новой кучи уже может не найтись АП, но в существующих места может хватить и на десятки тысяч таких блоков.
Re[8]: Пул из heap (куч). Имеет ли это смысл?
От: TheBeginner  
Дата: 11.06.23 15:29
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:


ЕМ>Если под смыслом понимается "будем счастливы, если удастся создать хоть один блок", то имеет. Если планируется продолжать сколько-нибудь длительную работу без освобождения памяти, то вряд ли.


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


Да, получается только для того редкого случая, когда адресное пространство процесса заполнено и удается втиснутся в свободный блок.

TB>>HeapCreate/HeapAlloc в большинстве случаев вернет NULL

EM> Вы понимаете, что они вернут нуль в достаточно разных условиях?

А в чем разница причин почему они могут вернуть NULL? Имхо, только доступная память. Есть лимит на количество куч в процессе, но он превышает реальные потребности.
Re[9]: Пул из heap (куч). Имеет ли это смысл?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 11.06.23 15:58
Оценка:
Здравствуйте, TheBeginner, Вы писали:

TB>А в чем разница причин почему они могут вернуть NULL? Имхо, только доступная память.


"Память" — это и адресное пространство, и реальная память, эти ресурсы независимы друг от друга. Плюс ограничение по размеру региона — если Вы насоздавали куч, затем насоздавали в них много блоков относительно небольшого размера, и забили все АП стандартными регионами по 256 кб, то создать блок в 300 кб будет негде. Плюс фрагментация каждой из куч.
Re[10]: Пул из heap (куч). Имеет ли это смысл?
От: TheBeginner  
Дата: 11.06.23 16:28
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>"Память" — это и адресное пространство, и реальная память, эти ресурсы независимы друг от друга. Плюс ограничение по размеру региона — если Вы насоздавали куч, затем насоздавали в них много блоков относительно небольшого размера, и забили все АП стандартными регионами по 256 кб, то создать блок в 300 кб будет негде. Плюс фрагментация каждой из куч.


"Память" это я обобщаю конечно. Судя по всему накладные расходы создания кучи — резервирование региона в адресном пространстве процесса в 64 Кб. Но это не отменяет вывода, что причина неудачи HeapCreate и HeapAlloc одна — переполнение адресного пространства или нехватка физической страничной памяти. Возможно выделение памяти в другой куче и закончится удачей и можно "поковылять" еще немного, но до этого доводить не надо.
Re[6]: Пул из heap (куч). Имеет ли это смысл?
От: Alekzander  
Дата: 11.06.23 22:55
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>А зачем? Такое полезно, если поделка наколенная, и лень возиться с учетом блоков, или когда блоков очень много, и поштучное освобождение настолько тормозит, что приходится жертвовать надежностью.


Valve давно штатно реализует во всех продуктах аварийное завершение на уровне GUI (на хоткее — оверлей, на оверлее — кнопка), потому, что никакого смысла ждать, пока отработают деструкторы и т.п., нет. Не наколенные поделки, вроде.
Re[7]: Пул из heap (куч). Имеет ли это смысл?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 12.06.23 06:37
Оценка:
Здравствуйте, Alekzander, Вы писали:

A>Valve давно штатно реализует во всех продуктах аварийное завершение на уровне GUI (на хоткее — оверлей, на оверлее — кнопка), потому, что никакого смысла ждать, пока отработают деструкторы и т.п., нет.


Если это именно аварийное завершение — в редких случаях, которые затем анализируются и исправляются, то вполне нормально. Если же такое делается штатно, и не для снижения фрагментации, а именно "чтоб не заморачиваться с деструкторами", то ой.
Re[8]: Пул из heap (куч). Имеет ли это смысл?
От: Alekzander  
Дата: 16.06.23 18:03
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

A>>Valve давно штатно реализует во всех продуктах аварийное завершение на уровне GUI (на хоткее — оверлей, на оверлее — кнопка), потому, что никакого смысла ждать, пока отработают деструкторы и т.п., нет.


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


А что "ой"? В том-то и дело, что это очень приятная фича, которую они массово внедрили. Причём, у них есть сериализация в облако и локально, и всё работает.

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

P.S. Пока я это писал, вышла новая версия Стима, в которой эту фичу сломали, выход через неё стал почти такой же тормозной, как обычный. Хотя до этого она работала много лет. Сглазил, не иначе.
Re[9]: Пул из heap (куч). Имеет ли это смысл?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 16.06.23 19:11
Оценка:
Здравствуйте, Alekzander, Вы писали:

A>Приятность фичи состоит в том, что абсолютно мгновенный выход и очень быстрый выход ощущаются юзером сильно по-разному


Сколько конкретно времени, по-Вашему, занимает "абсолютно мгновенный выход" и "очень быстрый выход"?

Сколько примерно должно быть уничтожаемых объектов, и что должны делать их деструкторы, чтобы за счет отказа от их вызова "очень быстрый выход" превратился в "абсолютно мгновенный"?

A>А проектировать в любом случае надо ресетоустойчивое поведение. Не надо ничего сохранять в OnExit().


То есть, деструкторы придумали глупцы?

В принципе, я не против "неаккуратного" освобождения, как такового, но только в случае адекватной оптимизации. То есть, когда вызов деструкторов исключается только в production-версии, а во время отладки все деструкторы вызываются, и применяются соответствующие методы проверки того, что объект не испорчен, блок памяти не используется после удаления и т.п.

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


Может, потому, что удаление объектов там реализовано неимоверно криво, как это нынче принято примерно везде?
Re[10]: Пул из heap (куч). Имеет ли это смысл?
От: Alekzander  
Дата: 17.06.23 02:35
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

A>>Приятность фичи состоит в том, что абсолютно мгновенный выход и очень быстрый выход ощущаются юзером сильно по-разному


ЕМ>Сколько конкретно времени, по-Вашему, занимает "абсолютно мгновенный выход" и "очень быстрый выход"?


Как я могу ответить, если у меня нет исходников и логгера? Ну, на глаз скажу — меньше 200 мс и 1000..2000 мс соответственно.

ЕМ>Сколько примерно должно быть уничтожаемых объектов, и что должны делать их деструкторы, чтобы за счет отказа от их вызова "очень быстрый выход" превратился в "абсолютно мгновенный"?


Я не работаю в Valve. К сожалению. А то бы я уже коммитил void OnExitButtonClick() { exit(0); }.

Тем не менее, я сильно подозреваю, что в деструкторах там какой-нибудь ::WaitForMultipleObjects() перед освобождением ресурсов, потому, что при загрузке карт (а это довольно частый юз-кейс для нажатия кнопки немедленного выхода из оверлея, потому что UI полностью блочится) пауза возрастает секунд до пяти и даже больше (!!!).

A>>А проектировать в любом случае надо ресетоустойчивое поведение. Не надо ничего сохранять в OnExit().


ЕМ>То есть, деструкторы придумали глупцы?


Пф-ф-ф-ф.

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

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

ЕМ>В принципе, я не против "неаккуратного" освобождения, как такового, но только в случае адекватной оптимизации. То есть, когда вызов деструкторов исключается только в production-версии, а во время отладки все деструкторы вызываются, и применяются соответствующие методы проверки того, что объект не испорчен, блок памяти не используется после удаления и т.п.


Я рассуждаю просто: если у меня замкнёт мою светодиодную ленту с левым БП и выключится компьютер, а программа и ОС это не отработают гарантированно стабильно, то зачем такие программа и ОС. А если отработают, то зачем сидеть и ждать. Л — Логика.

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


ЕМ>Может, потому, что удаление объектов там реализовано неимоверно криво, как это нынче принято примерно везде?


Я думаю, у них деградация со временем, как и везде. На смену смелым людям, запилившим классную фичу, пришли новички, которые увидели, что всё работает не так, как их учили и привели код в соответствие со своими шаблонными извилинами.
Re[11]: Пул из heap (куч). Имеет ли это смысл?
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 17.06.23 08:38
Оценка:
Здравствуйте, Alekzander, Вы писали:

A>Тем не менее, я сильно подозреваю, что в деструкторах там какой-нибудь ::WaitForMultipleObjects() перед освобождением ресурсов


То есть, проблема не в освобождении, как таковом, а в том, что оно выполняется неимоверно криво, и самый простой способ с этим справиться — замести под ковер.

A>сказал, что мы пришли к очень криповой ситуации. Десктопный софт при бесполезном закрытии тратит время на бесполезные деструкторы, что снижает отзывчивость, хотя и ОС, и программы всё умеют делать быстро и безопасно. Техлиды Майкрософт умеют смотреть правде в глаза.


Нет, они умеют назначать виновных. Умей они смотреть правде в глаза — перечислили бы навскидку с десяток куда более очевидных проблем, приводящих к тормозам везде и всюду. Но те проблемы трогать опасно — не дай бог выяснится, что тормозит как раз из-за перехода на технологию, которая несколько лет назад продвигалась в качестве передовой. А деструкторы были передовой технологией только в 70-80-х, с тех пор там не было никаких прорывов, так что можно наглядно продемонстрировать снижение тормозов за счет избавления от заведомо кривых деструкторов, но акцент сделать не на кривизне, а на якобы бесполезности деструкторов, как таковых.

A>если у меня замкнёт мою светодиодную ленту с левым БП и выключится компьютер, а программа и ОС это не отработают гарантированно стабильно, то зачем такие программа и ОС.


Не понял. Каким образом они могут "отработать" на выключенном компьютере? Это к чему вообще было?

A>Я думаю, у них деградация со временем, как и везде. На смену смелым людям, запилившим классную фичу, пришли новички, которые увидели, что всё работает не так, как их учили и привели код в соответствие со своими шаблонными извилинами.


О том и речь, что большинство новичков, неплохо разбираясь в парадигмах, шаблонах и паттернах, лишь отдаленно представляет себе, как это все работает "под капотом". В итоге получается классика.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.