Как улучшить производительность абстрактной фабрики, когда все время тратится на выделение памяти - PullRequest
1 голос
/ 17 ноября 2008

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

Примечания. Приложение уже использует специальный распределитель памяти, который выполняет пул. Компилятор VC ++ 6.0, а код использует старые коллекции RogueWave, а не STL.

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

Ответы [ 5 ]

3 голосов
/ 17 ноября 2008

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

1 голос
/ 17 ноября 2008

Вы пытались использовать пользовательский распределитель для объектов известного размера? Это хорошая техника для ускорения распределения.

0 голосов
/ 17 ноября 2008

Оператор можно заменить новым, даже для каждого класса. В вашем конкретном случае вы будете распределять и освобождать множество объектов одного класса. Это означает, что у вас будет довольно много выделений с одинаковым размером. Это шаблон, который легко оптимизировать. В основном, сохраните необработанный блок памяти, N * sizeof (T) и битовую карту с N / 8 битами, чтобы указать свободный статус. Это легко исправить; Ваш старый код не нужно менять.

0 голосов
/ 17 ноября 2008

Просто некоторые идеи ...

  1. Что вы делаете в своих ctors? Может они медленные? Большие объекты, доступ к файлам и т. Д.?

  2. Сколько памяти доступно для вашего приложения? Вы близки к физическим пределам, поэтому операционная система занята обменом?

  3. Будьте кеш дружественным, локализуя ваши шаблоны доступа к памяти. Если коллекции RogueWave не сделают этого для вас, возможно, пришло время искать что-то еще.

  4. Вы уверены, что ваш пользовательский распределитель не является виновником?

0 голосов
/ 17 ноября 2008

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

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

  • Если ваше приложение многопоточное, то распределитель памяти, скорее всего, будет выполнять некоторую синхронизацию. Но если вы можете организовать освобождение этих объектов в том же потоке, в котором они размещены, то они могут быть выделены из кучи для каждого потока, перегрузив оператор new. Это может быть быстрее: синхронизация не обязательно является настолько медленной, если нет конкуренции, но она занимает больше времени, чем ничего, и если другие потоки в вашем процессе выделяются одновременно, тогда возникнет конфликт.

  • Для еще более ограниченных сценариев использования, если вы обнаружите, что распределяете целую загрузку этих вещей, а затем всегда освобождаете их все сразу, когда закончите со списком, вы можете написать еще быстрее " gc-style "распределитель.

Оператор new / delete будет вызывать функции, подобные этим (и размер может быть константой, а не параметром, если есть только один класс, для которого вы используете пул):

char *alloc(POOL *pool, size_t size) {
    // if size is a parameter, and may be a non-multiple the max alignment 
    // requirement on your system, and you want this to work in general:
    // size = (size + MAX_ALIGNMENT - 1) % ALIGNMENT;
    char *block = pool.current;
    char *next = block + size;
    if (next > pool.limit) throw std::bad_alloc();
    pool.current = next;
    return block;
}

void free(char *block) {
    return;
}

void freeAll(POOL *pool) {
    pool.current = pool.start;
    return;
}

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

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