Эффективное использование нескольких распределителей - PullRequest
8 голосов
/ 11 марта 2012

Я исследовал переключение моего метода выделения с упрощения перегрузки нового на использование нескольких распределителей через базу кода.Тем не менее, как я могу эффективно использовать несколько распределителей?Единственный способ, которым я мог придумать в своих исследованиях, - это чтобы распределители были глобальными.Несмотря на то, что это, похоже, имеет проблемы, поскольку, как правило, это «плохая идея» - использовать множество глобальных переменных.

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

Ответы [ 3 ]

11 голосов
/ 19 марта 2012

В C ++ 2003 модель распределителя нарушена, и на самом деле не существует правильного решения.В C ++ 2011 модель распределителя была исправлена, и у вас могут быть распределители для каждого экземпляра, которые распространяются вплоть до содержащихся объектов (если, конечно, вы не решите заменить их).Как правило, для того, чтобы это было полезно, вы, вероятно, захотите использовать динамически полиморфный тип распределителя, который не требуется по умолчанию std::allocator<T> (и, как правило, я ожидаю, что он не будет динамически полиморфным, хотя это может быть лучшим вариантом реализации).Однако [почти] все классы в стандартной библиотеке C ++, которые выполняют выделение памяти, являются шаблонами, которые принимают тип распределителя в качестве аргумента шаблона (например, IOStreams являются исключением, но обычно они не выделяют какой-либо интересный объем памяти для гарантии добавления поддержки распределителя).).

В нескольких ваших комментариях вы настаиваете, что распределители должны быть глобальными: это определенно не правильно.Каждый тип, связанный с распределителем, хранит копию данного распределителя (по крайней мере, если у него есть какие-либо данные уровня экземпляра; если нет, то нет ничего для хранения, как, например, в случае с распределителем по умолчанию, использующим operator new() иoperator delete()).Это фактически означает, что механизм выделения, данный объекту, должен оставаться неизменным, пока есть активный распределитель, использующий его.Это может быть выполнено с использованием глобального объекта, но также может быть выполнено с использованием, например, подсчета ссылок или связывания распределителя с объектом, содержащим все объекты, которым он присвоен.Например, если каждый «документ» (например, XML, Excel, Pages, любой файл структуры) передает распределитель своим членам, распределитель может жить как член документа и уничтожаться при уничтожении документа после уничтожения всего его содержимого.,Эта часть модели распределителя должна работать с классами до C ++ 2011, если они также принимают аргумент распределителя.Однако в классах до C ++ 2011 распределитель не будет передаваться содержащимся объектам.Например, если вы назначите распределитель для std::vector<std::string>, то версия C ++ 2011 создаст std::string s, используя распределитель, данный std::vector<std::string>, соответствующим образом преобразованный для работы с std::string s.Этого не произойдет с распределителями до C ++ 2011.

Чтобы фактически использовать распределители в подсистеме, вам нужно будет эффективно передавать их как явно в качестве аргумента для ваших функций и / или классов, так и неявнопосредством объектов с распределителем, которые служат контекстом.Например, если вы используете какой-либо из стандартных контейнеров как [часть] передаваемого контекста, вы можете получить используемый распределитель, используя метод get_allocator().

3 голосов
/ 13 марта 2012

Вы можете использовать new размещение. Это может быть использовано либо для указания области памяти, либо для перегрузки типа static void* operator new(ARGS). Глобальные переменные не требуются, и это действительно плохая идея, если эффективность важна, а ваши проблемы требовательны. Конечно, вам нужно будет удерживать одного или нескольких распределителей.

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

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

2 голосов
/ 19 марта 2012

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

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

Фрагментация будет улучшена за счет распределения выделений с разными периодами жизни из разных куч - выделение фонового ввода-вывода в отдельной куче от активной задачи пользователей обеспечитдва не путают друг друга.Скорее всего, это возможно благодаря наличию стека для ваших куч и push / popping, когда вы находитесь в разных функциональных областях.

Промежуток в кеше будет улучшен за счет хранения распределений внутри системы.Распределение Quadtree / Octree происходит из их собственной кучи, что гарантирует локальность в рассмотрении запросов Frustrum.Лучше всего это сделать, перегрузив оператор new и оператор delete для определенных классов (OctreeNode).

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