Неожиданное потребление памяти

123
20 сентября 2019, 09:20

Пишу алгоритм бинарной сортировки (в целях обучения). Компилятор - MinGW-w64 с флагом оптимизации -o0

Дело в том, что мой код потребляет больше памяти чем я ожидаю..

Структура { ключ, значение, указатель на левого и правого предка } При использовании типов size_t в первом и втором значении в x64 системе логично предположить что итоговый размер структуры - 8+8+8+8 = 32 байта Но на деле при создании 100 000 000 объектов приложение кушает не 3.0гб а 4.5гб, что существенно выше ожидаемого.

Про выравнивание памяти читал и использование #pragma pack(push,1) в глобальной области никак не влияет на ситуацию (что впрочем логично, структура то ровная).

Так же я использовал дополнительную функцию для выделения памяти через malloc, так как мне подсказали что проблема может быть при вызовах new. К сожалению это так же не помогло, но по крайней мере дало возможность следить за размером выделяемой памяти во время исполнения.. Из чего я сделал вывод что размер выделяемой памяти корректен - 3 200 000 000 байт, по крайней мере на этапе выделения.

То есть вероятнее всего это всё таки какая-то оптимизация компилятора. Однако при создании простого массива 32 байтных структур приложение потребляет столько сколько нужно. Следовательно дело в логике выделения памяти (функция Insert(...))

Код который я использую: size_t memCount = 0;

template<typename Type, typename... Args>
Type* Alloc(Args... args)
{
    uint8* result = reinterpret_cast<uint8*>(malloc(sizeof(Type)));
    memCount += sizeof(Type);
    if(result)
        return new(reinterpret_cast<Type*>(result)) Type(args...);
    return nullptr;
}
template<typename kType, typename Type>
class Node
{
private:
    kType key;
    Type value;
    Node* left;
    Node* right;
public:
    explicit Node(const kType& key,
                  const Type& value) : key(key),
                                 value(value),
                                 left(nullptr),
                                 right(nullptr) { }
    ~Node()
    {
        if(left)  delete left;
        if(right) delete right;
    }
    const bool Insert(const kType& key, const Type& value, const kType& min, const kType& max)
    {
        if(min == max)
            return false;
        kType halfMax = (max - min) / 2 + min;
        if(key < halfMax)
        {
            if(!left)
            {
                left = Alloc<Node>(key, value);
                return true;
            }
            else
                return left->Insert(key, value, min, halfMax);
        }
        else
        {
            if(!right)
            {
                right = Alloc<Node>(key, value);
                return true;
            }
            else
                return right->Insert(key, value, halfMax, max);
        }
        return true;
    }
};
int main(void)
{
    Node<size_t, size_t>* arr = Alloc<Node<size_t, size_t>>(0, 0);
    for(size_t x = 1; x < 100000000; x++)
        arr->Insert(x, x, numeric_limits<size_t>::min(),
                          numeric_limits<size_t>::max());
    cout <<  "mem - "<< memCount << endl; // здесь видим 3 200 000 000 (2,9гб), как и должно было быть
    system("pause");
    return 0;
};
Answer 1

Наблюдаемоге поведение абсолютно логично. Вы не учитываете накладные расходы аллокатора и прочие скрытые "расходы" рантайма. Каждый вызов new обычно где-то сохраняет аттрибуты выделенной памяти - размер, положение (чтобы потом delete мог удалить корректно). Вот только в Ваших рассчетах это никак не отражено.

Если допустить, что под это отводится ещё байт 8 (но я не знаю ни компилятор, ни среду, поэтому могу только гадать), то общий размер потребляемой памяти приближается к наблюдаемому.

Что делать?

  • Попробовать другой аллокатор jmalloc, tmalloc.
  • Написать свой аллокатор:)
  • Использовать placement new. В результате можно будет выделить 3 гигабайта и разместить там все объекты (если только это 64битная система, на 32битной врядли)
  • Перестать беспокоиться и смириться.
READ ALSO
удаление объекта в C++ [дубликат]

удаление объекта в C++ [дубликат]

На данный вопрос уже ответили:

120
Прикрепить контроллер к FXML не через сам FXML

Прикрепить контроллер к FXML не через сам FXML

Собственно, есть у меня один FXML, к которому я, в зависимости от ситуации, хочу применять один из двух контроллеров, так что вариант с указанием...

107
Из-за чего появляется исключение?

Из-за чего появляется исключение?

Исключение указывает на эту строку StringuserJsonStroke=getJsonFromServer(Stringformat("%s/%d", urlPath), 0);

109