Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Менеджеры памяти


Автор: 3,14 10.7.2009, 08:12
Есть многопоточное приложение, к-ое каждую секунду создает и удаляет до 6000 объектов. Имеет ли смысл заменить создание/удаление объектов с помощью new/delete на использование менеджера памяти? Даст ли это какой то выигрыш?
Ссылки на литературу и результаты тестирования приветствуются.

Автор: maxim1000 10.7.2009, 08:37
по-хорошему, для таких решений стоит провести измерение времени, которое программа проводит в operator new и operator delete.

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

например, если где-то часто выделяются объекты одинакового размера - можно использовать аллокаторы для фиксированных блоков памяти, есть, например, в boost-е

Автор: Ln78 10.7.2009, 08:53
Думаю, что универсальный ответ дать не получится, зависит от конкретной задачи. 
Цитата(maxim1000 @  10.7.2009,  09:37 Найти цитируемый пост)
по-хорошему, для таких решений стоит провести измерение времени, которое программа проводит в operator new и operator delete.

А с учётом акцента на том, что приложение многопоточное, замерить время на использование объектов синхронизации. Т.е. получить 3 оценки для времени: без операторов выделения памяти и объектов синхронизации, с одним и со вторым.
Когда я занимался вычислительными задачами (там все вычисления проводились в одном отдельном потоке), в котором использовались объекты типа векторов и матриц, использование собственного менеджера памяти (его методы вызывались в конструкторах/деструкторах объектов) позволило существенно увеличить производительность. 

Автор: Lazin 10.7.2009, 10:05
Цитата(3 @ 14, 10.7.2009,  08:12 Найти цитируемый пост)
Есть многопоточное приложение, к-ое каждую секунду создает и удаляет до 6000 объектов.

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

Автор: Alexeis 10.7.2009, 10:22
  6000 объектов в секунду это не мало. Может имеет смысл переписать код так чтобы объекты создавались в стеке и уничтожались по выходу из функции? Если число объектов не известно, то можно воспользоваться функцией alloca для выделения в стеке участков произвольного размера и очистки по выходу, только нужно следить чтобы не было переполнения стека.

Автор: Lazin 10.7.2009, 11:40
размещать много данных в стеке - не самая лучшая идея, так как это приведет к потере производительности smile 

Автор: avnemchenko 10.7.2009, 12:29
Когда-то я создавал приложение, где с XML-файла бралась куча данных, на их основе строились новые. Там каждую секунду многомерные сложные конструкции динамически создавались, потом уничтожались (это было тестирование с большим количеством внешне задаваемых опций). И весьма скоро у меня все стало ВИСЕТЬ, по-черному  smile  ! Висело оно там из-за неучтенных new/delete, которые как-то друг на друга накладывались.
Тогда я озверел, за недельку написал менеджер памяти. И в итоге 1) исчезли все проблемы с зависанием, т. к. все происходило в моей памяти, и не было динамических обращений к чему-то за ее пределами, 2) также я смог проконтролировать всякие перекрытия и их устранить, и 3) быстродействие ощутимо подскочило. Так что рекомендую!
Как потом оказалось, у меня были проблемы с созданием текстовых строк - иной раз одно накладывалось на другое, или вообще в пустоту ссылалось...
Теперь я везде использую эту свою библиотеку. Она у меня многопоточная (использую критические секции из Win API), стабильно и быстро работает. Теперь, правда, возникла неожиданная проблема - я себе резервирую кучу памяти. А когда я подключаю к своему приложению несколько своих же DLL-ек, и все они тоже резервируют кучу памяти, ее в итоге не хватает ... Буду дальше ее совершенствовать.
А тем, кто не любит изобретать велосипеды, лучше брать что-то из Boost.

Автор: Леопольд 10.7.2009, 12:39
Цитата(3 @ 14,10.7.2009,  08:12)
Есть многопоточное приложение, к-ое каждую секунду создает и удаляет до 6000 объектов. Имеет ли смысл заменить создание/удаление объектов с помощью new/delete на использование менеджера памяти? Даст ли это какой то выигрыш?
Ссылки на литературу и результаты тестирования приветствуются.

На мой взгляд рекомендация Lazin о пуле лучшая. Я, наверное, перегрузил бы new - delete с использованием пула. И использовал бы "умные" указатели что бы не "парится" об исключениях.

Добавлено @ 12:43
Цитата(avnemchenko @ 10.7.2009,  12:29)
А тем, кто не любит изобретать велосипеды, лучше брать что-то из Boost.

Мне кажется это нарвится всем...  А в boost'е, как минимум, отдебажено не одним пользователем. Больше времени и внимания можно уделить остальному коду.

Автор: avnemchenko 10.7.2009, 13:23
Цитата(Леопольд @ 10.7.2009,  12:39)
Добавлено @ 12:43
Цитата(avnemchenko @ 10.7.2009,  12:29)
А тем, кто не любит изобретать велосипеды, лучше брать что-то из Boost.

Мне кажется это нарвится всем...  А в boost'е, как минимум, отдебажено не одним пользователем. Больше времени и внимания можно уделить остальному коду.

Угу. А вот насчет доверия другим библиотекам - было дело, в фирме писали мы компилятор здоровенный (пишется он на трех континентах сотней разработчиков). И там они использовали реализацию std:: от Silicon Graphics - там были одни заморочки 9глюки и баги). Потом подключили реализацию от Microsoft - там оказались другие баги. И меня это так задолбало, что я решил делать СВОЮ библиотеку, где будут МОИ баги! И я их буду исправлять в режиме реального времени, а не ждать месяцами, когда там прочухается поставщик библиотеки!

Не спорю - это не самый быстрый путь. Зато самый надежный.

Автор: vinick 10.7.2009, 13:30
Цитата(Lazin @  10.7.2009,  11:40 Найти цитируемый пост)
размещать много данных в стеке - не самая лучшая идея, так как это приведет к потере производительности

Можно узнать почему? Я всегда считал, что работа со стеком происходит гораздо быстрее чем с кучей.

Автор: Alexeis 10.7.2009, 13:59
vinick, думаю тут дело в алгоритмах, стековый объект нельзя вернуть по ссылке, только копию, но я имел ввиду построить алгоритм так чтобы то что должно быть выделено выделялось вместе со входом в функцию и после обработки уничтожалось, без того чтобы его копировать. 

Автор: Lazin 10.7.2009, 15:08
Цитата(Alexeis @  10.7.2009,  13:59 Найти цитируемый пост)
vinick, думаю тут дело в алгоритмах, стековый объект нельзя вернуть по ссылке, только копию, но я имел ввиду построить алгоритм так чтобы то что должно быть выделено выделялось вместе со входом в функцию и после обработки уничтожалось, без того чтобы его копировать
если стека не хватает, то генерируется исключение и стек либо расширяется, либо, если это невозможно - программа падает, поэтому, хранить большие объемы данных в стеке - не правильно, для этого есть куча smile 

Автор: 3,14 10.7.2009, 15:15
Хм, а у меня вопрос как замерить производительность. Идея в том что сейчас проблем не наблюдается. Но пока нет основной функциональности, к-ая давала бы основную нагрузку. Я боюсь что возникнут проблемы на дальнейших стадиях, но просто так переписывать все на те же пулы тоже не хочется, а вдруг выигрыш будет пустяковый на самом деле? 
Собственно вопрос в том проводились ли какие либо тесты на этот счет и как протестировать что то подобное у себя? Могут ли возникнуть какие нибудь узкие места с многопоточным менеджером памяти у C++, и как результат лишние затраты на синхрониззацию?

Автор: Alexeis 10.7.2009, 16:51
Цитата(Lazin @  10.7.2009,  14:08 Найти цитируемый пост)
если стека не хватает, то генерируется исключение и стек либо расширяется, либо, если это невозможно - программа падает, поэтому, хранить большие объемы данных в стеке - не правильно, для этого есть куча

  Во-первых в настройках компилятора есть параметр - размер стека, вычисляешь сколько тебе максимум надо, умножаешь в 2 раза и ставишь как максимальный размер. Во-вторых если стек будет возрастать, то уже уменьшаться не будет, поэтому временные затраты на расширение стека это разовые затраты. Их не нужно учитывать.
  ОЗУ оно и в Африке ОЗУ, будет ли это стек или пул. Если юзать кучу то быстрее точно не будет. Факт в том что объект живет короткое время, а затраты на одну операцию выделения значительны. Уже есть встроенный механизм, не вижу смысла зачем его дублировать своим? 

Автор: Леопольд 10.7.2009, 18:57
Цитата(avnemchenko @ 10.7.2009,  13:23)
...
Угу. А вот насчет доверия другим библиотекам - ... - там были одни заморочки 9глюки и баги).
...
И я их буду исправлять в режиме реального времени, а не ждать месяцами, когда там прочухается поставщик библиотеки!

Не спорю - это не самый быстрый путь. Зато самый надежный.

Не соглашусь...

Просто надо пользовать заслужившие доверие open source библиотеки. Например, boost, wxWidgets... Думаю, их можно пользовать спокойно, именно потому что уже отдебажено другими пользователями. К тому же, если всё же есть бага в такой библиотеке, то её можно дебажить самому, в режиме реального времени... Обычно это быстрее чем написать свою библиотеку с высокой степенью надёжностью, такой-же сложности.

Автор: Lazin 10.7.2009, 21:58
Alexeis, 
Цитата(Alexeis @  10.7.2009,  16:51 Найти цитируемый пост)
Во-первых в настройках компилятора есть параметр - размер стека, вычисляешь сколько тебе максимум надо, умножаешь в 2 раза и ставишь как максимальный размер. Во-вторых если стек будет возрастать, то уже уменьшаться не будет, поэтому временные затраты на расширение стека это разовые затраты. Их не нужно учитывать.
в стеке удобно размещать что-либо небольшое, а так-же что-либо, имеющее фиксированный размер smile 

Цитата(Alexeis @  10.7.2009,  16:51 Найти цитируемый пост)
ОЗУ оно и в Африке ОЗУ, будет ли это стек или пул. Если юзать кучу то быстрее точно не будет. Факт в том что объект живет короткое время, а затраты на одну операцию выделения значительны. Уже есть встроенный механизм, не вижу смысла зачем его дублировать своим?  

речь идет не о дублировании, существует множество стратегий управления памятью
к примеру, в realtime системах как правило происходит какая-то обработка данных, выделяется память под множество объектов, а после того как обработка закончена, все созданные объекты разрушаются и система возвращается в исходное состояние, после чего все снова повторяется. При этом может фрагментироваться память, могут быть значительные задержки, а самое главное - сложно будет добиться того, что-бы каждая итерация укладывалась в определенные временные рамки. 
Как правило, для того, что-бы это исправить, абсолютно все временные объекты размещают в одном большом буфере, выделенном заранее в хипе. Ну и разрушить все сразу то-же не проблема, достаточно освободить буфер, предварительно вызвав деструкторы(хотя это не всегда нужно), можно даже не освобождать буфер а использовать его повторно. Если во время обработки данных что-то пойдет не так и, к примеру, произойдет выход за границу массива, то шансов испортить кучу - мало. Так как буфер большой, и маловероятно, что ты выйдешь за его границы.
Если ты будешь использовать для этого стек, то помимо данных он естественно будет содержать и коды возврата, и параметры ф-ий. И если во время обработки данных программа испортит память, то велика вероятность, что стек будет испорчен то-же.
в общем, ты получишь примерно такой call stack(растет вверх):
Код

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

короче, все яйца в одну корзину лучше не класть smile 

Автор: Alexeis 10.7.2009, 22:05
Цитата(Lazin @  10.7.2009,  20:58 Найти цитируемый пост)
Как правило, для того, что-бы это исправить, абсолютно все временные объекты размещают в одном большом буфере, выделенном заранее в хипе. Ну и разрушить все сразу то-же не проблема, достаточно освободить буфер, предварительно вызвав деструкторы(хотя это не всегда нужно), можно даже не освобождать буфер а использовать его повторно.

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

Автор: Леопольд 10.7.2009, 23:48
Цитата(Alexeis @ 10.7.2009,  22:05)
Цитата(Lazin @  10.7.2009,  20:58 Найти цитируемый пост)
... можно даже не освобождать буфер а использовать его повторно.

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

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

Автор: 3,14 19.7.2009, 08:27
Как бы это странно не звучало, но заметной нагрузки на проц создание/удаление такой кучи объектов вроде не дает. В чем тогда прикол использования менеджеров памяти?

Автор: MAKCim 19.7.2009, 08:52
Цитата(3 @ 14, 19.7.2009,  08:27 Найти цитируемый пост)
В чем тогда прикол использования менеджеров памяти? 

фрагментация?

Автор: DrHex 19.7.2009, 08:59
Короче чем меньше вызовов new\call тем быстрее.

А вообще нужно писать объекты которые могут быть использованны много раз.

Автор: Леопольд 19.7.2009, 11:36
Цитата(3 @ 14,19.7.2009,  08:27)
Как бы это странно не звучало, но заметной нагрузки на проц создание/удаление такой кучи объектов вроде не дает. В чем тогда прикол использования менеджеров памяти?

А по времени выполнения? Профайлер что показывает?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)