Экстремальное использование памяти для индивидуального динамического распределения

вот простой тест, который я сделал на MSVC++ 2010 под Windows 7:

// A struct with sizeof(s) == 4, e.g 4 bytes
struct s
{
    int x;
};

// Allocate 1 million structs
s* test1 = new s[1000000];

// Memory usage show that the increase in memory is roughly 4 bytes * 1000000 - As expected
// NOW! If I run this:
for (int i = 0; i < 1000000; i++)
    new s();

// The memory usage is disproportionately large. When divided by 1000000, indicates 64 bytes per s!!!

Это общеизвестно или я что-то упускаю? Раньше я всегда создавал объекты на лету, когда это было необходимо. Например, new Triangle() для каждого треугольника в сетке и т. д.

Действительно ли существуют накладные расходы порядка величины для динамического распределения памяти отдельных экземпляров?

Ваше здоровье

РЕДАКТИРОВАТЬ:

Только что скомпилировал и запустил ту же программу на Windows XP с помощью g++: теперь накладные расходы составляют 16 байт, а не 64, как было раньше! Очень интересно.


person Vadim    schedule 19.07.2010    source источник


Ответы (4)


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

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

person Brian Hooper    schedule 19.07.2010
comment
Что ж, проблема очевидна и для гораздо более крупных объектов. Исходная проблема была для объектов размером около 50-80 байт. Именно тогда я начал расследование и провел тест массива против индивидуального распределения. - person Vadim; 19.07.2010
comment
Хм. Я пойду и посмотрю, что происходит на моей машине. - person Brian Hooper; 19.07.2010
comment
Пробовал; использование для таких объектов составляет 4 байта (оптом) или 32 байта (отдельно) для объектов, содержащих int. Расширив объект до 16 int, я получил 64 байта на объект (оптом) и 78 байт на объект (по отдельности). Это говорит мне о 16 байтах накладных расходов на выделение и выравнивание по 16-байтовым границам; какая-то фигура такого порядка, во всяком случае. Я использую Ubuntu 10.4, кстати. - person Brian Hooper; 19.07.2010

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

person Blindy    schedule 19.07.2010
comment
И релизные, и отладочные сборки. Просто чтобы убедиться, что я попробовал 32-битную систему и все еще имею значительные накладные расходы для отдельных распределений. Конечно, должны быть некоторые накладные расходы, но я предполагаю, что они не должны быть такими большими. - person Vadim; 19.07.2010

Обычно при любом отдельном выделении памяти возникают накладные расходы. Теперь это из моих знаний о malloc, а не new, но я подозреваю, что это одно и то же.

Раздел области памяти, вырезанный для выделения (скажем) 30 байтов, обычно будет иметь заголовок (например, 16 байтов, и все подобные цифры приведены только в качестве примеров ниже, они могут быть другими) и может быть дополнен до кратного 16 байтам для облегчения управления ареной.

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

Он содержит минимальную информацию о размере блока и может также иметь защиту памяти (для обнаружения порчи арены).

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

Я ответил на связанный вопрос здесь с более подробной информацией. Я подозреваю, что для C++ потребуется дополнительная информация заголовка, поскольку ему, вероятно, придется хранить количество элементов сверх размера раздела (для правильных вызовов деструктора), но это просто предположение с моей стороны. Это не влияет на тот факт, что для каждого выделенного элемента необходима какая-либо учетная информация.

Если вы действительно хотите увидеть, для чего используется пространство, вам нужно изучить исходный код среды выполнения MSVC.

person paxdiablo    schedule 19.07.2010
comment
Да, это имеет смысл. Я предполагаю, что по какой-то причине я зарезервировал больше отступов, заголовков и т.д. Итак, каковы общие рекомендации по динамическому размещению нескольких объектов? Причина индивидуального распределения заключается в том, чтобы обойти вектор «Суперкласс», где нельзя использовать подклассы push_back. поэтому я использую vector‹SuperClass*› Теперь я рассматриваю подклассы vector‹SubClass›... - person Vadim; 19.07.2010
comment
Ну, я следую правилу: если я собираюсь выделить только несколько, я выделяю их по отдельности. Потраченная память на меня особо не влияет. Если я собираюсь выделить много, я выделяю набор из них (например, массив или какой-либо абстрактный тип данных), чтобы минимизировать накладные расходы. Не уверен, как решить вашу конкретную векторную проблему, большая часть моей работы - это либо C, либо Java, где я, вероятно, написал бы свой собственный векторный материал, если бы не было ничего подходящего :-) - person paxdiablo; 19.07.2010

Вы должны проверить реализацию malloc. Наверное, это прояснит ситуацию.

Не уверен, однако, что malloc MSVC++ можно где-то посмотреть. Если нет, посмотрите на какую-нибудь другую реализацию, возможно, они в какой-то степени похожи.

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

person Albert    schedule 19.07.2010