компактный вектор небольшой отпечаток стопы памяти - PullRequest
1 голос
/ 19 марта 2012

Я поддерживаю старое приложение C ++, в котором много классов, как показано ниже:

    class ClassWithALotOfVectors
    {
        std::vector<int> _vector1;
        std::vector<int> _vector2;

        // a lot of other vector datamembers go here

        std::vector<double> _vectorN;
    };

то есть - тип данных, в котором содержатся векторы double или int. Дело в том, что эти векторы никогда не заполняются одновременно, и, следовательно, когда мы создаем 100000 экземпляров ClassWithALotOfVectors - использование памяти складывается до внушительного числа, хотя только 10% этих векторов используются.

Поэтому я решил написать небольшой класс векторов «выделить по требованию».

Это обёртка вокруг std :: vector - где внутренний вектор создается только когда доступ в первый раз (с использованием двух методов get - ref () и const_ref ())

Когда я заменил std :: vector в классе ClassWithALotOfVectors на compact_vector как показано ниже:

    class ClassWithALotOfVectors2
    {
    compact_vector<int> _vector1;
    compact_vector<int> _vector2;

    compact_vector<double> _vectorN;
    };

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

Можете ли вы взглянуть на мою реализацию compact_vector и посмотрите, можете ли вы обнаружить что-то не так с управлением памятью.

    template <class T> class compact_vector 
    { 
    public: 
            compact_vector<T>()
            :_data(NULL) 
            { 
            } 

            ~compact_vector<T>() 
            { 
                    clear(); 
            } 

            compact_vector<T>(const compact_vector<T> & rhs) 
            :_data(NULL) 
            { 
                    if (NULL != rhs._data) 
                    { 
                            _data = new std::vector<T>(*(rhs._data)); 
                    } 
                    else 
                    { 
                            clear(); 
                    } 
            } 

            //      assignment 
            // 
            compact_vector<T> & operator=(const compact_vector<T> & rhs) 
            { 
                    if (this != &rhs) 
                    { 
                            clear(); 
                            if (NULL != rhs._data) 
                            { 
                                    _data = new std::vector<T>(*rhs._data); 
                            } 
                    } 
                    return *this; 
            } 
            compact_vector<T> & operator=(const std::vector<T> & rhs) 
            { 
                    clear(); 
                    if (!rhs.empty()) 
                    { 
                            _data = new std::vector<T>(rhs); 
                    } 
                    return *this; 
            } 

            const std::vector<T> & const_ref() 
            { 
                    createInternal(); 
                    return *_data; 
            } 
            std::vector<T> & ref() 
            { 
                    createInternal(); 
                    return *_data; 
            } 
            void    clear() 
            { 
                    if (NULL != _data) 
                    { 
                            _data->clear(); 
                            delete _data; 
                            _data = NULL; 
                    } 
            } 
    private: 
            void    createInternal() 
            { 
                    if (NULL == _data) 
                    { 
                            _data = new std::vector<T>(); 
                    } 
            } 

    private: 
            compact_vector<T>(const std::vector<T> & rhs) 
            { 
            } 
            compact_vector<T>(std::vector<T> & rhs) 
            { 
            } 
            compact_vector<T>(std::vector<T> rhs) 
            { 
            } 

            std::vector<T> * _data; 
    }; 

Ответы [ 4 ]

3 голосов
/ 19 марта 2012

Большинство реализаций std::vector не получают память, пока она вам не понадобится, а размер вектора обычно составляет всего несколько (3 + возможно дополнительная отладочная информация) указателей.То есть std::vector<int>() не будет выделять место для какого-либо объекта.Я полагаю, что вы здесь лаете не на то дерево.

Почему в вашем исходном коде гораздо больше памяти?Вы ожидаете, что clear() освободит память?

Если это так, вы ошибаетесь.Функция clear() уничтожает содержащиеся элементы, но не освобождает выделенную память.Попробуйте добавить оболочку, чтобы очистить память, которая использует следующую идиому:

std::vector<int>().swap( _data );

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

Кроме того, если ваш компилятор поддерживает C ++ 11, вы можете использовать shrink_to_fit() после вызова clear().Стандарт не требует shrink_to_fit() для фактического сжатия, чтобы соответствовать , но для пустого вектора я бы ожидал, что реализация это сделает.Проверьте это, позвонив capacity() после вызова и выяснив, понизилось ли оно до 0 или нет.

1 голос
/ 19 марта 2012

Используйте умные указатели . В этом случае std::unique_ptr с ручной копией ctor / оператора присваивания.Внезапно все ваши проблемы решены.Это как волшебство!

Извините, что звучит так странно, но утечки памяти всегда имеют одинаковый ответ: умный указатель.

0 голосов
/ 07 октября 2015

Идея о том, что одновременно используется только один вектор, звучит как концепция union. Конечно, в C ++ есть элемент, называемый anonymous union, который делает именно это.

class ClassWithALotOfVectors {
    public:
    union  {
        std::vector<double> _vector1;
        std::vector<double> _vector2;
    };
    ClassWithALotOfVectors() { new(&_vector1) std::vector<double>; };
    ~ClassWithALotOfVectors() { _vector1.~vector<double>(); };
};

Поскольку объединение не может знать, какой конструктор элемента вызывать, конструкторы по умолчанию отключены в объединении, и вам придется вручную создавать и восстанавливать элемент объединения, но я думаю, что это выполнит то, что вы ищете, алиасинг _vector1 и _vector2 к одному и тому же элементу, но помещая оба в пространство имен ClassWithALotOfVectors, затем освобождая вектор, когда вызывается деструктор для ClassWithALotOfVectors.

Подробнее об анонимных союзах: Ссылка CPP: Анонимные Союзы

0 голосов
/ 20 марта 2012

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

class ClassWithALotOfVectors    
{    
   std::vector<int> _vector1;     
   std::vector<int> _vector2;        
   // a lot of other vector datamembers go here         
   std::vector<double> _vectorN;

  public:
     ClassWithALotOfVectors() : Init() { }   
     void Init()   
     {   
         vector1.reserve(0);   
         /// Like wise other vectors.   
     }  
};
...