Я думаю, что STL заставляет мое приложение утроить использование памяти

Я ввожу файл 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);
}
10.12.2008 19:20:12
Какой STL вы используете? На какой машине?
Edouard A. 10.12.2008 19:22:45
Какое использование памяти у очень-очень маленького файла?
DJClayworth 10.12.2008 20:02:08
Просто чтобы вы знали, что вам не нужно разделять вызов open с помощью fstreams, вы можете просто сделать: std :: ifstream file ("what", std :: ios :: binary); также, когда объект ifstream разрушен, он также автоматически закрывается. Поэтому обычно вам не нужно явно закрывать.
Evan Teran 11.12.2008 05:38:12
Также ваша переменная inFile в main абсолютно бессмысленна, поскольку конструктор std :: string, который принимает const char *, не является явным. Это означает, что передача const char * в функцию, которая принимает std :: string, будет работать автоматически.
Evan Teran 11.12.2008 05:41:51
Также! "тетср (& tempStr [0], и ул [indexVec [lpVar03]], Memsize);" выглядит очень капризно для меня, я не юрист по стандартам, но я не уверен, что std :: string гарантированно будет смежным внутри (только то, что c_str / data возвращает непрерывный буфер.
Evan Teran 11.12.2008 05:45:26
14 ОТВЕТОВ
РЕШЕНИЕ

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

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

  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));
  }
5
10.12.2008 19:41:16

Попробуйте использовать список вместо вектора. Векторы (почти всегда) линейны в памяти.

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

0
10.12.2008 19:32:21
Список может просто использовать больше памяти. Он должен хранить два дополнительных указателя для каждого элемента списка. Это также будет намного медленнее выполнять итерации, из-за ошибок в кеше.
jalf 10.12.2008 19:45:34
Полностью согласен, list будет использовать больше памяти, если .reserve () используется с vector.
Drakosha 10.12.2008 19:49:37
Я на самом деле не уверен, но я считаю, что для большинства реализаций C ++ класс std :: string не реализуется с использованием метода подсчета ссылок, копирования при записи.
Michael Burr 10.12.2008 19:51:25
Правильно - его проблема в том, что он не знает, сколько символов новой строки, поэтому он не может сделать резерв. Поскольку он не может сделать резервирование, он потенциально фрагментирует память, делая push_back порядка миллиона раз (при условии 200 символов на строку.)
Matt Cruikshank 10.12.2008 20:20:47

Я не знаю, относится ли это к делу, потому что я не знаю, как выглядит ваш файл.

Но вы должны знать, что при хранении очень короткой строки std :: string может иметь значительные накладные расходы. И если вы индивидуально начинаете использовать char * для очень коротких строк, вы также увидите все накладные расходы блока выделения.

Сколько строк вы помещаете в этот вектор, и какова их средняя длина?

0
10.12.2008 19:32:51

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

1
10.12.2008 19:40:20
Но ifstream не должен хранить все содержимое файла в памяти. Это просто буфер.
Roddy 10.12.2008 19:42:04
@Roddy: Конечно, это буфер, но какого размера? Что ограничивает это? Поскольку iostream действительно является абстракцией над базовыми функциями ОС, вам придется заглянуть в реализации более низкого уровня, чтобы увидеть, что они делают, когда их просят открыть файл. Держу пари, большинство из них загружают все это в память.
Harper Shelby 10.12.2008 19:50:14
@ Харпер !!! ??? А? Вы действительно думаете, что большинство файлов открытия ОС загружает все файлы в память? В самом деле? Ты правда так думаешь? Почему?
Will Dean 10.12.2008 23:15:14
@ Уилл Дин: просто верх понятия о том, что я давно не делал - я застрял настолько далеко от кода, как этот, что я работаю над памятью, открывая большие файлы с помощью iostream, и получаю его высосать память.
Harper Shelby 11.12.2008 23:15:27

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

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

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

3
10.12.2008 19:53:43
Существует много этого кода, который можно заменить на более идиоматический C ++, более правильно используя STL.
Harper Shelby 10.12.2008 19:51:33
  1. не используйте std :: list. Это потребует больше памяти, чем вектор.
  2. vector выполняет то, что называется «удвоением», т. е. когда не хватает места, он выделяет вдвое больше памяти, чем в данный момент. чтобы избежать этого, вы можете использовать метод std :: vector :: reserve (), и если я не ошибаюсь, вы можете проверить это с помощью метода std :: vector :: acity () (note Capacity ()> = size () ).

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

1
23.05.2017 10:27:52
мы не знаем количество строк заранее, поэтому использование reserve () не сработает, при использовании резервного есть вероятность, что вектор все равно будет перераспределен
SMeyers 10.12.2008 19:57:06
Вы можете изменить его размер, поменяв временную копию после того, как закончите чтение. stackoverflow.com/questions/253157/how-to-downsize-stdvector
user44511 11.12.2008 06:08:23
Удвоенное распределение, которое делает вектор, не удваивает объем памяти, используемой строками - только размер массива, хранящегося в векторе - память, используемая строками, хранится в куче
1800 INFORMATION 18.12.2008 21:47:37

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

-1
10.12.2008 19:49:02

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

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

1
10.12.2008 19:56:23

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

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

2
22.12.2009 23:38:28

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

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

2
10.12.2008 20:25:38

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

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

Вот что я придумал: 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
11.12.2008 05:10:08

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

0
11.12.2008 05:43:28

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

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

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

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

0
17.07.2012 17:45:32

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

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

0
18.12.2008 21:52:03