Почему я должен использовать новое место размещения?

Как кажется, placement new создает новый объект в предварительно распределенной памяти, так значит ли это, что потребуется меньше времени? Похоже, он быстрее распределяется с использованием старого обычного new. Тогда, если это так удобно и быстрее, почему бы не использовать placement new все время?

Ответ 1

нормальная (замещающая) new в основном эквивалентна выполнению

T* ptr = static_cast<T*>(malloc(sizeof(T)));
new(ptr) T;

Конечно, реальность выглядит немного по-другому из-за ошибок и таковых, но результат более или менее одинаковый (через не идентичный, вы не можете delete указатель, выделенный таким образом, вместо этого вам нужно вызвать destructor явно (ptr->~T()), а затем отпустите память с помощью free).

Таким образом, размещение нового действительно должно быть быстрее, чем не размещение нового, так как ему не нужно выделять память. Однако проблема в том, что память должна быть где-то выделена. Таким образом, вы по существу заменили один вызов на new на вызов placement new и некоторый код для выделения где-нибудь (если не почему вы использовали бы new в первую очередь?). Должно быть очевидно, что это менее удобно и подвержено большей ошибке.

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

Конечно, используются для размещения новых (иногда у вас есть память, предварительно распределенная), но это просто не обычный случай

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

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

Это не единственное использование этого нового оператора. Подробнее здесь.

Также помните, что новое место размещения не вызывает деструктор автоматически! Вы должны сделать foo->~Foo(); для своего Foo foo; вручную.

Ответ 5

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

Ответ 6

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

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

Так что посмотри, что тебе нужно; если вы выделяете тонну одного и того же объекта, да, размещение нового может быть быстрее. Но всего несколько объектов? Я бы не стал беспокоиться.

Тем не менее, это не всегда вопрос времени. Например, вы можете использовать новое размещение, чтобы гарантировать выравнивание ваших объектов в куче. Вы можете сделать что-то вроде:
void* buffer = aligned_malloc(sizeof(Object), 16); 
Object* object = new (buffer) Waypoint();

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