Использование памяти структур данных STL, windows vs. linux

У меня есть программа, которая сильно использует std::map. В Windows гораздо больше памяти используется под Linux. У кого-нибудь есть идея, почему это происходит?

Linux: Last process took 42.31 s and used not more than 909 MB (RSS 900 MB) of memory

Окна: Last process took 75.373 s and used not more than 1394 MB (RSS 1395 MB) of memory

Я использую gcc 4.4.3 и компилятор VS 2010 С++ в командной строке с настройками выпуска.

EDIT: Извините за ответ на вопросы, которые поздно...

Код выглядит следующим образом:

enum Symbol {
    ...
}

class GraphEntry {

    public:

    ...

    virtual void setAttribute (Symbol name, Value * value) = 0;

    const Value * attribute (Symbol name) const;

    private:

    std::map<Symbol, Attribute> m_attributes;
};

class Attribute {

    public:

    Attribute (Symbol name, Value * val);

    ...

    Symbol name () const;

    Value * valuePointer () const;

    void setValuePointer (Value * p);

    private:

    Symbol m_name;

    Value * m_value;
};

class Graph : public GraphEntry {

    ...

    public:

    Node * newNode (...);

    Graph * newSubGraph (...);

    Edge * newEdge (...);

    ...

    setSomeAttribute (int x);

    setSomeOtherAttribute (float f);

    ...

    private:

    std::vector<GraphEntry *> m_entries;
};

Все это описывает структуру графа, которая может содержать некоторые атрибуты на своих узлах и ребрах. Value - это просто базовый класс, а производные классы могут содержать значения с произвольными типами, такими как int или std::string.

ИЗМЕНИТЬ 2: В Windows я использую следующие флаги: -DRELEASE -DNDEBUG -DQT_NO_DEBUG -DQT_NO_DEBUG_OUTPUT -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -DNOMINMAX /O2 /MD /Gy /EHsc

ИЗМЕНИТЬ 3: Использование памяти считывается из файла /proc под linux (например, memuse). В Windows вызывается несколько функций WinAPI, но я не эксперт для этого, так что все, что я могу сказать об этом.

ИЗМЕНИТЬ 4: Использование /GS- и -D_SECURE_SCL приводит к Last process took 170.281 s and used not more than 1391 MB (RSS 1393 MB) of memory

Ответ 1

Вы заметите, что использование памяти в Windows где-то между 1 и 2 раза больше. Heap алгоритмы в стороне, Windows malloc(), а затем любые структуры данных, выделенные в куче через new (например, std::map узлы со стандартным типом распределителя) выровнены с 16 байтами. В Linux, glibc по умолчанию имеет 8-байтовое выравнивание. Предполагая некоторое сглаживание различий из-за фрагментации, оптимизацию получения неиспользуемых страниц и т.д., Вы можете ожидать, что различия станут менее очевидными.

Быстрая проверка кода указывает, что ключ карты и типы значений должны быть соответственно 4 и 8 байт (Symbol и Attribute). Они будут содержать до 8 байтов в Linux и 16 байт в Windows. У вас должно быть равное количество узлов карты, по крайней мере, в реализации MSVC, они должны потреблять минимум 22 байта, который MSVC расширит до 32 из-за правил выравнивания его членов, что также является его гранулярностью в malloc. GCC расширит его до 24, что означает приблизительное общее количество 48 байтов в MSVC для GCC/Linux 32 на node. Примерно на 50% больше использования памяти в Windows.

Здесь структура node, используемая в MSVC, я могу найти эквивалент GCC, если вы заинтересованы:

struct _Node
    {   // tree node
    _Nodeptr _Left; // left subtree, or smallest element if head
    _Nodeptr _Parent;   // parent, or root of tree if head
    _Nodeptr _Right;    // right subtree, or largest element if head
    value_type _Myval;  // the stored value, unused if head
    char _Color;    // _Red or _Black, _Black if head
    char _Isnil;    // true only if head (also nil) node

Я добавлю для тех, кто не знаком с тем, как работает память, есть несколько факторов:

  • Память распределяется в кусках, округляющихся до следующего кратного выравнивания для используемого механизма распределения. Для кучи используются правила выравнивания malloc() (если вы не подорвете обычную кучу или не используете какой-либо другой распределитель, чем по умолчанию).
  • Виртуальная память "предоставляется" системой в кусках, известных как страницы, целые кратные размеру кадра, что выходит за рамки этого вопроса. Это незначительно влияет на ответ, так как использование памяти настолько велико по сравнению с размером страницы (4K), а размер страницы в свою очередь настолько массивен по сравнению с используемыми настройками (8 и 16).

Ответ 2

Каждый компилятор поставляется с собственной реализацией STL, поэтому вы сравниваете:

  • Подпрограммы распределения GCC STL + Linux
  • Процедуры выделения VС++ STL + Windows

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

Я полагаю, что вы не сравниваете 32-битную программу с 64-битной программой, так как это будет еще менее значимым.

Ответ 3

В некоторых версиях VС++ также используются проверенные итераторы (_SECURE_SCL) в сборках релизов. VC2005 и VC2008 включили их по умолчанию. VC2010 отключает их по умолчанию

В зависимости от вашего компилятора, это может быть еще одна вещь, чтобы проверить (и отключить).

Ответ 4

Вы выполнили тест в режиме отладки или отладки в окнах? STL в режиме отладки делает много дополнительной проверки; возможно, он также использует больше памяти для выполнения всех проверок.

Ответ 5

Для VС++ попробуйте использовать /GS - переключатель командной строки.

Ответ 6

Когда вы говорите, что используемая память "не более" относится к использованию пиковой памяти или к среднему использованию памяти в течение всего срока службы приложения?

Убедитесь, что память, выделенная вашим приложением с использованием "нового" или "malloc" или любого другого вызова библиотеки памяти, освобождается с помощью "delete" или "free" или любого эквивалентного вызова библиотеки.

В Linux вы можете использовать valgrind и проверять утечки памяти.