использование памяти в классе - преобразование double в float не уменьшило использование памяти, как ожидалось - PullRequest
1 голос
/ 01 августа 2011

Я инициализирую миллионы классов следующего типа

template<class T>
struct node
{
  //some functions
private:
  T m_data_1;
  T m_data_2;
  T m_data_3;

  node* m_parent_1;
  node* m_parent_2;
  node* m_child;
}

Цель шаблона - дать пользователю возможность выбрать точность float или double, при этом идея на node<float> будет занимать меньше памяти (RAM).

Однако, когда я переключаюсь с double на float, объем памяти моей программы не уменьшается, как я ожидаю. У меня два вопроса,

  1. Возможно ли, что компилятор / операционная система резервирует больше места, чем требуется для моих операций с плавающей запятой (или даже хранит их как двойные). Если так, как я могу остановить это - я использую Linux на 64-битной машине с g ++.

  2. Существует ли инструмент, позволяющий мне определять объем памяти, используемый всеми различными классами? (т. е. какое-то профилирование памяти) - чтобы убедиться, что память не перегружена где-то еще, о чем я даже не думал.

Ответы [ 3 ]

5 голосов
/ 01 августа 2011

Если вы компилируете для 64-битной версии, то каждый указатель будет иметь размер 64-битной версии.Это также означает, что их может потребоваться выровнять по 64-битным.Поэтому, если вы храните 3 числа с плавающей запятой, возможно, придется вставить 4 байта заполнения.Таким образом, вместо сохранения 12 байтов вы сохраняете только 8. Заполнение все равно будет там, независимо от того, находятся ли указатели в начале структуры или в конце.Это необходимо для того, чтобы поместить последовательные структуры в массивы, чтобы продолжать поддерживать выравнивание.

Кроме того, ваша структура в основном состоит из 3 указателей.Сохраняемые 8 байтов переносят вас из 48-байтового объекта в 40-байтовый объект.Это не совсем значительное снижение.Опять же, если вы компилируете для 64-битных.

Если вы компилируете для 32-битных, то вы экономите 12 байтов из 36-байтовой структуры, что лучше в процентном отношении.Потенциально больше, если дубликаты должны быть выровнены до 8 байтов.

3 голосов
/ 01 августа 2011

Остальные ответы верны относительно источника несоответствия.Однако указатели (и другие типы) в x86 / x86-64 не должны быть выровнены.Просто производительность лучше, когда они есть, поэтому GCC сохраняет их выравнивание по умолчанию.

Но GCC предоставляет атрибут «упакованный», который позволяет вам контролировать это:

#include <iostream>

template<class T>
struct node
{
private:
    T m_data_1;
    T m_data_2;
    T m_data_3;

    node* m_parent_1;
    node* m_parent_2;
    node* m_child;
}    ;

template<class T>
struct node2
{
private:
    T m_data_1;
    T m_data_2;
    T m_data_3;

    node2* m_parent_1;
    node2* m_parent_2;
    node2* m_child;
} __attribute__((packed));

int
main(int argc, char *argv[])
{
    std::cout << "sizeof(node<double>) == " << sizeof(node<double>) << std::endl;
    std::cout << "sizeof(node<float>) == " << sizeof(node<float>) << std::endl;
    std::cout << "sizeof(node2<float>) == " << sizeof(node2<float>) << std::endl;
    return 0;
}

В моей системе (x86-64, g ++ 4.5.2) эта программа выводит:

sizeof(node<double>) == 48
sizeof(node<float>) == 40
sizeof(node2<float>) == 36

Конечно, механизм «атрибута» и сам атрибут «упакованы» зависят от GCC.

1 голос
/ 01 августа 2011

В дополнение к действительным пунктам, которые Николь делает:

Когда вы вызываете new / malloc, это не обязательно соответствует 1 к 1 с вызовом ОС для выделения памяти. Это связано с тем, что для уменьшения количества дорогих системных вызовов диспетчер кучи может выделить больше запрошенного, а затем «перераспределить» фрагменты этого при вызове new / malloc. Кроме того, память может быть выделена только 4 КБ за раз (как правило, это минимальный размер страницы). По существу, могут быть выделенные фрагменты памяти, которые в настоящее время не используются активно, чтобы ускорить будущие выделения.

Чтобы ответить на ваши вопросы напрямую:

1) Да, среда выполнения, скорее всего, выделит больше памяти, чем вы просили - но эта память не будет потрачена впустую, она будет использоваться для будущих новостей / malloc, но все равно будет отображаться в «диспетчере задач» или любом другом инструменте ты используешь. Нет, это не будет способствовать удвоению поплавков. Чем больше выделений вы сделаете, тем меньше вероятность того, что это граничное условие станет причиной разницы в размерах, и элементы Никола будут доминировать. При меньшем количестве выделений этот элемент, вероятно, будет доминировать (где «большой» и «маленький» полностью зависит от вашей ОС и ядра).

2) Диспетчер задач Windows выдаст вам общий объем выделенной памяти. Что-то вроде WinDbg фактически даст вам фрагменты диапазона виртуальной памяти (обычно выделяемые в виде дерева), которые были выделены во время выполнения. Я предполагаю, что для Linux эти данные будут доступны в одном из файлов в каталоге / proc, связанном с вашим процессом.

...