2008-09-19 1 views
1
  1. В приложении у нас есть что-то около 30 типов объектов, которые создаются неоднократно.
  2. Некоторые из них имеют долгую жизнь (часы), некоторые из них имеют короткие (миллисекунды).
  3. Объекты могут быть созданы в одном потоке и уничтожены в другом.

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

Приложение 1.

1.1. Распределение пула объектов/памяти для одного типа обычно не относится к другому типу (см. 1.3 для исключения).

1.2. Распределение памяти выполняется только для одного типа (класса) во времени, обычно для нескольких объектов во времени.

1.3. Если тип агрегирует другой тип, используя указатель (по какой-то причине), эти типы выделяются вместе в одной непрерывной части памяти.

Приложение 2.

2.1. Известно, что использование коллекции с сериализацией доступа для каждого типа хуже, чем новое/удаление.

2.2. Приложение используется на разных платформах/компиляторах и не может использовать трюки для компилятора/платформы.

Append 3.

Становится очевидным, что реализация быстро (с минимальной задержкой) следует организовать объект объединения в звездообразной сети заводов. Там, где центральная фабрика является глобальной для других конкретных отраслей. Регулярное предоставление/рециркуляция объектов более эффективно делать на фабрике с конкретным потоком, в то время как центральный завод может использоваться для балансировки объектов между потоками.

3.1. Каков наиболее эффективный способ организации связи между центральным заводом и фабриками, зависящими от резьбы?

+0

Знаете ли вы заранее время жизни объекта или передаете потоки? – hazzen 2008-09-19 05:41:38

+0

К сожалению нет. Это определяется логикой приложения. – user18547 2008-09-19 06:29:38

ответ

1

Если вы не просмотрели tcmalloc, вы можете взглянуть. Основываясь на реализации вашей концепции, это может быть хорошим началом. Ключевые моменты:

  • Определите набор классов размеров. (Каждое распределение будет выполнено с использованием записи с равным или большим размером.)
  • Используйте один класс размера на страницу. (Все экземпляры на странице имеют одинаковый размер.)
  • Использовать для поточных фриланшей, чтобы избежать атомных операций над каждым alloc/dealloc.
  • Когда избыточный список в потоке слишком велик, переместите некоторые экземпляры обратно в центральный фрилайст. Попробуйте переместить выделение с той же страницы.
  • Когда пустая строка в потоке пуста, возьмите часть из центрального фрилиста. Попробуйте сделать смежные записи.
  • Важно: Возможно, вы это знаете, но убедитесь, что ваш проект минимизирует ложное разделение.

Дополнительные вещи, которые вы можете сделать это tcmalloc не может:

  • Попробуйте включить локальность ссылок с использованием более мелкозернистой пулы распределения. Например, если к нескольким тысячам объектов будут обращаться вместе, то лучше всего, если они будут близки друг к другу в памяти. (Чтобы свести к минимуму ошибки пропущенных кешей и TLB.) Если вы выделяете эти экземпляры из собственного потока, то они должны иметь довольно хорошую локальность.

  • Если вы заранее знаете, какие экземпляры будут долговечными, а какие нет, то выделите их из отдельных потоков потоков. Если вы не знаете, то периодически копируйте старые экземпляры с помощью threadcache для размещения и обновления старых ссылок на новые экземпляры.

2

Чтобы свести к минимуму задержку конструкции/разрушения, вам понадобятся полностью построенные объекты, поэтому вы исключите новое/ctor/dtor/delete время. Эти «свободные» объекты могут храниться в списке, чтобы вы просто нажали элемент в конце.

Вы можете заблокировать пулы объектов (по одному для каждого типа) один за другим. Он немного эффективнее, чем системная блокировка, но не имеет накладных расходов на блокировку объекта.

+0

Если ctor и dtor не выделяют или не освобождают память, тогда они должны быть быстрыми, и поэтому не должно быть причин их избегать. Кроме того, проблема с наличием одного freelist для каждого типа заключается в том, что вы должны использовать атомарные ops для манипулирования им, и это будет способствовать ложному совместному использованию. – 0124816 2008-09-19 07:38:47

+0

Не уверен, что это лучший ответ. ИМХО было лучше о tcmalloc. Не могу проголосовать, хотя – user18547 2008-09-19 16:29:30

3

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

Если вы все еще хотите сделать пул объектов, в качестве первого шага вы должны обеспечить, чтобы ваши объекты были неактивными, это было бы необходимым условием для повторного использования объекта. Аналогичным образом вы должны убедиться, что члены объекта и сам объект не имеют проблемы с использованием других потоков, отличных от тех, которые его создали. (COM STA-объекты/оконные дескрипторы и т. Д.)

Если вы используете окна и COM, одним из способов использования системного пула будет писать объекты Free Threaded и включать объединение объектов, что сделает время выполнения COM + (ранее известное как МТС) сделают это за вас. Если вы используете какую-то другую платформу, например Java, возможно, вы можете использовать серверы приложений, которые определяют интерфейсы, которые должны реализовывать ваши объекты, и сервер COM + мог бы объединить вас.

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

Если вам нужно свернуть собственный код, создайте динамически растущую коллекцию, которая отслеживает уже созданные объекты. Используйте вектор предпочтительнее для коллекции, так как вы только добавляете в коллекцию, и было бы легко пройти его поиск свободного объекта. (предполагая, что вы не удаляете объекты в пуле). Измените тип коллекции в соответствии с политиками удаления (вектор указателей/ссылок на объекты, если вы используете C++, чтобы удалить и воссоздать объект в том же месте)

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

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

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

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

Затем можно оптимизировать на этой конструкции оттуда.

Надеюсь, что помогает.

0

Если у вас есть предположение о предпочтительном размере пула, вы можете создать пул фиксированного размера, используя структуру стека, используя массив (самое быстрое решение). Затем вам необходимо реализовать четыре этапа жесткой инициализации жизненного цикла объекта (и распределения памяти), мягкую инициализацию, мягкую очистку и жесткую очистку (и выпуск памяти). Теперь в псевдокоде:

Object* ObjectPool::AcquireObject() 
{ 
    Object* object = 0; 
    lock(_stackLock); 
    if(_stackIndex) 
     object = _stack[ --_stackIndex ]; 
    unlock(_stackLock); 
    if(!object) 
     object = HardInit(); 
    SoftInit(object); 
} 

void ObjectPool::ReleaseObject(Object* object) 
{ 
    SoftCleanup(object); 
    lock(_stackLock); 
    if(_stackIndex < _maxSize) 
    { 
     object = _stack[ _stackIndex++ ]; 
     unlock(_stackLock); 
    } 
    else 
    { 
     unlock(_stack); 
     HardCleanup(object); 
    } 
} 

метод HardInit/HardCleanup выполняет полную инициализацию объекта и разрушения, и они выполняются только в случае, если пул пуст или если освобожденный объект не может соответствовать бассейн, потому что она полна. SoftIniti выполняет мягкую инициализацию объектов, он инициализирует только те аспекты объектов, которые могут быть изменены с момента его выпуска. SoftCleanup метод свободных ресурсов, используемых объектом, который должен быть освобожден как можно быстрее или те ресурсы, которые могут стать недействительными в течение времени, когда его владелец находится в пуле. Как вы видите, блокировка минимальная, только две строки кода (или только несколько инструкций).

Эти четыре метода могут быть реализованы в отдельных (шаблонных) классах, чтобы вы могли реализовать точно настроенные операции для каждого типа объекта или использования. Также вы можете использовать интеллектуальные указатели для автоматического возврата объекта в свой пул, когда он больше не нужен.

+0

Проблемы с этим в том, что вы увидите много разногласий по блокировке стека. Кроме того, существует ложное совместное использование, так как распределение из отдельных потоков будет смежным. Кроме того, если выделено 3000 временных рядов, а затем выделен 1 постоянный объект, то 3000 временных рядов не будут восстановлены. – 0124816 2008-09-19 07:42:29

0

Вы пробовали hoard allocator? Он обеспечивает лучшую производительность, чем распределитель по умолчанию на многих системах.

0

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

В любом случае, если вы еще не начали внедрять это, по крайней мере, вы можете поместить функциональность create/destroy за интерфейс, чтобы вы могли проверить/изменить/оптимизировать это позже, когда у вас есть дополнительная информация о том, что на самом деле делает ваша система.