Я думаю, что STL заставляет мое приложение утроить использование памяти - PullRequest
2 голосов
/ 10 декабря 2008

Я ввожу файл 200 МБ в свое приложение, и по очень странной причине использование памяти моим приложением составляет более 600 МБ. Я пробовал vector и deque, а также std :: string и char * безрезультатно. Мне нужно, чтобы использование памяти моим приложением было почти таким же, как у файла, который я читаю, любые предложения были бы чрезвычайно полезны. Есть ли ошибка, которая вызывает такое большое потребление памяти? Не могли бы вы точно определить проблему или мне все это переписать?

Windows Vista SP1 x64, Microsoft Visual Studio 2008 SP1, 32-разрядная версия, процессор Intel

Целое приложение до сих пор:

#include <string>
#include <vector>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <iterator>
#include <algorithm>
#include <time.h>



static unsigned int getFileSize (const char *filename)
{
    std::ifstream fs;
    fs.open (filename, std::ios::binary);
    fs.seekg(0, std::ios::beg);
    const std::ios::pos_type start_pos = fs.tellg();
    fs.seekg(0, std::ios::end);
    const std::ios::pos_type end_pos = fs.tellg();
    const unsigned int ret_filesize (static_cast<unsigned int>(end_pos - start_pos));
    fs.close();
    return ret_filesize;
}
void str2Vec (std::string &str, std::vector<std::string> &vec)
{
    int newlineLastIndex(0);
    for (int loopVar01 = str.size(); loopVar01 > 0; loopVar01--)
    {
        if (str[loopVar01]=='\n')
        {
            newlineLastIndex = loopVar01;
            break;
        }
    }
    int remainder(str.size()-newlineLastIndex);

    std::vector<int> indexVec;
    indexVec.push_back(0);
    for (unsigned int lpVar02 = 0; lpVar02 < (str.size()-remainder); lpVar02++)
    {
        if (str[lpVar02] == '\n')
        {
            indexVec.push_back(lpVar02);
        }
    }
    int memSize(0);
    for (int lpVar03 = 0; lpVar03 < (indexVec.size()-1); lpVar03++)
    {
        memSize = indexVec[(lpVar03+1)] - indexVec[lpVar03];
        std::string tempStr (memSize,'0');
        memcpy(&tempStr[0],&str[indexVec[lpVar03]],memSize);
        vec.push_back(tempStr);
    }
}
void readFile(const std::string &fileName, std::vector<std::string> &vec)
{
    static unsigned int fileSize = getFileSize(fileName.c_str());
    static std::ifstream fileStream;
    fileStream.open (fileName.c_str(),std::ios::binary);
    fileStream.clear();
    fileStream.seekg (0, std::ios::beg);
    const int chunks(1000); 
    int singleChunk(fileSize/chunks);
    int remainder = fileSize - (singleChunk * chunks);
    std::string fileStr (singleChunk, '0');
    int fileIndex(0);
    for (int lpVar01 = 0; lpVar01 < chunks; lpVar01++)
    {
        fileStream.read(&fileStr[0], singleChunk);
        str2Vec(fileStr, vec);
    }
    std::string remainderStr(remainder, '0');
    fileStream.read(&remainderStr[0], remainder);
    str2Vec(fileStr, vec);      
}
int main (int argc, char *argv[])
{   
        std::vector<std::string> vec;
        std::string inFile(argv[1]);
        readFile(inFile, vec);
}

Ответы [ 14 ]

5 голосов
/ 10 декабря 2008

Ваша память фрагментирована.

Попробуйте что-то вроде этого:

  HANDLE heaps[1025];
  DWORD nheaps = GetProcessHeaps((sizeof(heaps) / sizeof(HANDLE)) - 1, heaps);

  for (DWORD i = 0; i < nheaps; ++i) 
  {
    ULONG  HeapFragValue = 2;
    HeapSetInformation(heaps[i],
                       HeapCompatibilityInformation,
                       &HeapFragValue,
                       sizeof(HeapFragValue));
  }
3 голосов
/ 10 декабря 2008

Если я правильно понял, самая большая проблема заключается в том, что этот алгоритм автоматически удваивает вдвое необходимую память.

В ReadFile () вы читаете весь файл в набор строк размером «singleChunk», а затем в последнем цикле в str2Vec () вы выделяете временную строку для каждого сегмента, разделенного символом новой строки. Итак, вы удваиваете память прямо здесь.

У вас также есть проблемы со скоростью - str2vec делает 2 прохода по чанку, чтобы найти все новые строки. Там нет причин, вы не можете сделать это в одном.

2 голосов
/ 10 декабря 2008

Еще одна вещь, которую вы можете сделать, это загрузить весь файл в один блок памяти. Затем создайте вектор указателей на первый символ каждой строки и одновременно замените символ новой строки на \ 0, чтобы он заканчивался нулем. (Предполагается, конечно, что в ваших строках не должно быть \ 0.)

Это не обязательно так же удобно, как иметь вектор строк, но наличие вектора const char * потенциально «так же хорошо».

2 голосов
/ 10 декабря 2008

Контейнеры STL существуют для абстрагирования операций с памятью. Если у вас жесткий предел памяти, вы не сможете абстрагироваться от него.

Я бы рекомендовал использовать mmap() для чтения файла в (или, в Windows, MapViewOfFile()).

1 голос
/ 11 декабря 2008

Я думаю, что ваша попытка написать собственную стратегию буферизации неверна.

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

Вот что я придумал: NB протестировал текстовую версию «Библии короля Джеймса», которую я нашел в Интернете.

#include <string>
#include <vector>
#include <list>
#include <fstream>
#include <algorithm>
#include <iterator>
#include <iostream>

class Line: public std::string
{
};

std::istream& operator>>(std::istream& in,Line& line)
{
    // Relatively efficient way to copy a line into a string.
    return std::getline(in,line);
}
std::ostream& operator<<(std::ostream& out,Line const& line)
{
    return out << static_cast<std::string const&>(line) << "\n";
}

void readLinesFromStream(std::istream& stream,std::vector<Line>& lines)
{
    /*
     * Read into a list as this is flexible in memory usage and will not
     * allocate huge chunks of un-required space.
     *
     * Even with huge files the space for list will be insignificant
     * compared to the size of the data.
     *
     * This then allows us to reserve the correct size of the vector
     * Thus avoiding huge memory chunks being prematurely allocated that
     * are not required. It also prevents the internal structure from
     * being copied every time the container is re-sized.
     */
    std::list<Line>     data;
    std::copy(  std::istream_iterator<Line>(stream),
                std::istream_iterator<Line>(),
                std::inserter(data,data.end())
             );

    /*
     * Reserve the correct size in the vector.
     * then copy out of the list into the vector
     */
    lines.reserve(data.size());
    std::copy(  data.begin(),
                data.end(),
                std::back_inserter(lines)
             );
}

void readLinesFromFile(std::string const& name,std::vector<Line>& lines)
{
    /*
     * Set up the file stream and override the default buffer used by the stream.
     * Make it big because we think the istream buffer is insufficient!!!!
     */
    std::ifstream       file;
    std::vector<char>   buffer(10000);
    file.rdbuf()->pubsetbuf(&buffer[0],buffer.size());

    file.open(name.c_str());
    readLinesFromStream(file,lines);
}


int main(int argc,char* argv[])
{
    std::vector<Line>   lines;
    readLinesFromFile(argv[1],lines);

    // Un-comment if your file is larger than 1100 lines.

    // I tested with a copy of the King James bible. 
    // std::cout << "Lines: " << lines.size() << "\n";
    // std::copy(lines.begin() + 1000,lines.begin() + 1100,std::ostream_iterator<Line>(std::cout));
}
1 голос
/ 10 декабря 2008

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

Во-вторых, кроме ваших (по какой-то причине?) Статических переменных, единственными данными, которые не освобождаются, когда вы закончите читать файл, является вектор. Поэтому проверьте его емкость и проверьте емкость каждой строки, которую он содержит. Узнайте, сколько памяти они используют каждый. У вас есть инструменты, чтобы определить, где память расходуется.

1 голос
/ 10 декабря 2008
  1. не использовать std :: list. Это потребует больше памяти, чем вектор.
  2. vector выполняет то, что называется «удвоением», то есть, когда не хватает места, он выделяет дважды памяти, которую он имеет в данный момент. во избежание этого вы можете использовать метод std :: vector :: reserve (), и если я не ошибаюсь, вы можете проверить его, используя std :: vector :: acity ( ) метод (примечание емкость ()> = размер ()).

Поскольку количество строк неизвестно во время выполнения, я не вижу простого алгоритма, чтобы избежать проблемы удвоения. Из комментария slavy13.myopenid.com решение состоит в том, чтобы переместить информацию в другой сохраненный вектор после окончания чтения (соответствующий вопрос Как уменьшить std :: vector? ).

1 голос
/ 10 декабря 2008

Внутри readFile у вас есть как минимум 2 копии вашего файла - ifstream и данные, скопированные в ваш std :: vector. Пока у вас открыт файл и вы копируете его, как вы, будет сложно сократить общий объем памяти, который будет вдвое меньше размера файла.

0 голосов
/ 19 декабря 2008

Я считаю, что лучший способ сделать строки - это карта памяти, доступная только для чтения. Не надо писать \ 0 в for \ n, вместо этого используйте пары const char * s, такие как std::pair<const char*, const char*> или пары const char* s и счетчик. Если вам нужно отредактировать строки, хороший способ сделать это создать объект, который может хранить пары указателей или std :: string с измененной строкой.

Что касается сохранения места в памяти с помощью векторов STL или deques, хороший метод - позволить ему удвоиться, пока вы не закончите добавление к нему. Затем измените его размер до реального, что должно освободить неиспользуемую память обратно в распределитель кучи. Память все еще может быть выделена для программы, хотя я бы не беспокоился об этом. Кроме того, вместо того, чтобы брать размер по умолчанию, начните с получения размера файла в байтах, разделите на ваши лучшие предположения среднее число символов в строке и зарезервируйте столько места в начале.

0 голосов
/ 11 декабря 2008

Вы должны знать, что, поскольку вы объявили fileStream как static, оно никогда не выходит за рамки, то есть файл не закрывается до самого последнего момента выполнения. Это, безусловно, потребует немного памяти. Вы можете явно закрыть его прямо перед этим последним str2Vec, чтобы попытаться помочь ситуации.

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

Черт, я сомневаюсь, что вы действительно нуждаетесь , чтобы узнать размер файла, как вы делаете здесь, вы можете просто читать в больших количествах "куски", пока не получите короткое чтение (в этот момент вы закончите) .

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

...