Работа с переменными-членами std::string/std::vector при использовании boost::singleton_pool
Я пишу приложение, критичное к производительности, в котором я создаю большое количество объектов аналогичного типа для размещения заказов. Я использую boost:: singleton_pool для выделения памяти. Наконец мой класс выглядит следующим образом.
class MyOrder{
std::vector<int> v1_;
std::vector<double> v2_;
std::string s1_;
std::string s2_;
public:
MyOrder(const std::string &s1, const std::string &s2): s1_(s1), s2_(s2) {}
~MyOrder(){}
static void * operator new(size_t size);
static void operator delete(void * rawMemory) throw();
static void operator delete(void * rawMemory, std::size_t size) throw();
};
struct MyOrderTag{};
typedef boost::singleton_pool<MyOrderTag, sizeof(MyOrder)> MyOrderPool;
void* MyOrder:: operator new(size_t size)
{
if (size != sizeof(MyOrder))
return ::operator new(size);
while(true){
void * ptr = MyOrderPool::malloc();
if (ptr != NULL) return ptr;
std::new_handler globalNewHandler = std::set_new_handler(0);
std::set_new_handler(globalNewHandler);
if(globalNewHandler) globalNewHandler();
else throw std::bad_alloc();
}
}
void MyOrder::operator delete(void * rawMemory) throw()
{
if(rawMemory == 0) return;
MyOrderPool::free(rawMemory);
}
void MyOrder::operator delete(void * rawMemory, std::size_t size) throw()
{
if(rawMemory == 0) return;
if(size != sizeof(Order)) {
::operator delete(rawMemory);
}
MyOrderPool::free(rawMemory);
}
Недавно я опубликовал вопрос о повышении производительности при использовании boost:: singleton_pool. Когда я сравнил производительность boost:: singleton_pool и распределителя по умолчанию, я не получил никакого выигрыша в производительности. Когда кто-то указал, что в моем классе есть члены типа std:: string, распределение которых не регулируется моим пользовательским распределителем, я удалил переменные std:: string и перезапустил тесты. На этот раз я заметил значительное повышение производительности.
Теперь в моем реальном приложении я не могу избавиться от переменных-членов времени std:: string и std:: vector. Должен ли я использовать boost:: pool_allocator с моими переменными-членами std:: string и std:: vector?
boost:: pool_allocator выделяет память из базового std:: singleton_pool. Будет ли иметь значение, если разные переменные-члены (у меня есть несколько типов std:: string / std:: vector в моем классе MyOrder. Также я использую пулы для классов, отличных от MyOrder, которые содержат типы std:: string / std:: vector как члены тоже) использовать один и тот же пул памяти? Если это так, как я могу убедиться, что они делают так или иначе?
2 ответа
- Теперь в моем реальном приложении я не могу избавиться от переменных-членов времени std:: string и std:: vector. Должен ли я использовать boost:: pool_allocator с моими переменными-членами std:: string и std:: vector?
Я никогда не рассматривал эту часть boost, но если вы хотите изменить место, где строки распределяют свою память, вам нужно передать другой распределитель std::basic_string<>
во время компиляции. Другого пути нет. Однако вы должны знать о недостатках этого: например, такие строки не могут быть назначены std::string
больше. (Хотя с использованием c_str()
будет работать, это может наложить небольшой штраф на производительность.)
- boost:: pool_allocator выделяет память из базового std:: singleton_pool. Будет ли иметь значение, если разные переменные-члены (у меня есть несколько типов std:: string / std:: vector в моем классе MyOrder. Также я использую пулы для классов, отличных от MyOrder, которые содержат типы std:: string / std:: vector как члены тоже) использовать один и тот же пул памяти? Если это так, как я могу убедиться, что они делают так или иначе?
Весь смысл пула состоит в том, чтобы поместить в него более одного объекта. Если бы это был только один, вам бы не нужен бассейн. Так что, да, вы можете поместить в него несколько объектов, включая динамическую память нескольких std::string
объекты.
Однако покажет, получит ли это какой-либо прирост производительности. Вы используете пул, потому что у вас есть основания полагать, что он быстрее, чем распределитель общего назначения (вместо того, чтобы использовать его, например, для выделения памяти из определенной области, например, совместно используемой памяти). Обычно такой пул быстрее, потому что он может делать предположения о размере объектов, размещенных в нем. Это, безусловно, верно для вашего MyOrder
class: его объекты всегда имеют одинаковый размер, иначе (большие производные классы) вы не будете размещать их в пуле.
Это отличается для std::string
, Весь смысл использования динамически размещаемого строкового класса заключается в том, что он адаптируется к любой длине строки. Куски памяти, необходимые для этого, имеют разный размер (в противном случае вы могли бы просто вместо этого использовать символьные массивы). Я вижу мало места для распределителя пула, чтобы улучшить по сравнению с распределителем общего назначения для этого.
На заметку: Ваш перегружен operator new()
возвращает результат вызова глобального, но ваш operator delete
просто пропускает что-нибудь, идущее к этому пулу free()
, Это кажется мне очень подозрительным.
Использование собственного распределителя для std::string
/std::vector
в вашем классе будет работать (при условии, что распределитель правильный) - но только тестирование производительности покажет, действительно ли вы видите какую-либо выгоду от него.
В качестве альтернативы, если вы знаете, что std::string
/std::vector
будет иметь верхние пределы, вы можете реализовать тонкую оболочку вокруг std::array
(или обычный массив, если у вас нет C++11), что делает его заменой.
Даже если размер не ограничен, если есть некоторый размер, который будет иметь большинство значений, вы можете расширить std::array
основанные выше реализации, чтобы быть расширяемыми путем распределения с помощью вашего пулированного распределителя, если они заполняются.