У меня есть приложение, которое в основном выглядит так:
Init();
while(true){
read_data();
process_data();
write_processed_data();
}
данные, выделенные в прохождении цикла, полностью удаляются, как только мы закончим write_processed_data. Более того, количество выделений в каждом проходе цикла ограничено константой.
Я думал, что мог бы выделить, используя что-то, используя новое размещение на подвижном указателе
внутри блока фиксированного размера, а затем в конце прохода цикла я просто
сбросить «подвижный указатель» на начало блока, тем самым полностью исключив удаление.
Однако мои попытки не были очень успешными, кажется, что труднее достичь, чем
изначально планировалось.
Это общая стратегия распределения?
В такой программе я мог набирать скорость и не беспокоиться об утечках памяти.
Если у вас есть верхняя граница, вам вообще не нужна куча.
Это лучшее решение.
Спасибо @EdHeal за указание на основную идею следующего подхода:
Перед вашим циклом в вашем блоке памяти «распределите» достаточно большие массивы всех конкретных типов данных, которые вам нужно будет использовать. Используйте их в качестве набора пулов объектов. В отличие от типичной реализации пулов объектов, вам никогда не нужно увеличивать свои пулы, потому что при создании массивов в начале вы знаете верхнюю границу числа объектов каждого типа, которые вы можете использовать. (Ваша постановка проблемы подразумевает, что вы располагаете этой информацией, по крайней мере, перед тем, как войти в цикл.)
Во время цикла каждый раз, когда вам нужен новый объект, получите его указатель из его пула. Вы можете реализовать функцию, которая помещает указатель обратно в пул, чтобы снова использовать его как «новый» объект, или если есть известное ограничение на общее количество «новых распределений», которое не слишком много больше, чем в верхнем течении. Количество объектов, используемых одновременно, вам даже не нужно беспокоиться о том, чтобы возвращать объекты обратно в пул. (Второй вариант сделает управление пулом очень простым, так как все, что вам нужно сделать, это вести учет количества объектов, которые были взяты из каждого пула, и это говорит вам, какой объект является следующим «новым».) В в любом случае, в конце цикла вы сбрасываете пулы, чтобы все объекты снова были «новыми».
Я бы, наверное, создал класс под названием что-то вроде Allocator
для управления пулами, с функцией для каждого типа объектов, которые он может взять из пула или вернуть обратно в пул и reset()
функция для вызова в конце цикла. Там могут быть более элегантные способы сделать работу.
Обратите внимание, что если вы достаточно точно знаете верхние границы требуемого числа объектов, заблаговременно, вы можете создать все вышеупомянутые структуры данных, не помещая ничего в кучу.
Одним из способов избежать большого количества ненужных новостей и удалений является использование пула объектов и переопределение операторов new и delete класса. В принципе:
// In header file
class Thing{...}
// In .cc file
namespace thingpool {
static std::vector<Thing*>* chunks = 0;
}
void* Thing::operator new(size_t size){
// If pool is not initialized
if (!thingpool::chunks){
thingpool::chunks = new std::vector<Thing*>;
thingpool::chunks->reserve(100); // or some upper bound on expected Things
}
void* memptr;
if(!thingpool::chunks->empty()) {
memptr = thingpool::chunks->back();
thingpool::chunks->pop_back();
}
else {
memptr = ::malloc(size);
}
return memptr;
}
// put memory back in pool
void Thing::operator delete(void* ptr)
{
if (thingpool::chunks)
thingpool::chunks->push_back(static_cast<key*>(ptr));
}
И очистить пул вещей в какой-то момент.