2013-10-12 2 views
2

Я читаю три большие двоичные файлы (каждый один c.180Mb) в станде :: вектор следующим образом:Bad Ошибка выделения при наполнении зОго :: вектора

m_ifStream.open("myfile.dat", std::ios::binary | std::ios::in); 

if (m_ifStream) 
{ 
    //Obtain input stream length 
    m_ifStream.seekg (0, ios::end); 
    streamLength = (size_t)(m_ifStream.tellg()); 
    m_ifStream.seekg (0, ios::beg); 

    //Reserve doesn't work around the problem, may be more efficient though... 
    //m_buffer = new vector<unsigned char>(); 
    //m_buffer->reserve(streamLength); 

    //Next line sometimes results in bad_alloc when reading a large file 
    m_buffer = new vector<unsigned char>((std::istreambuf_iterator<char>(m_ifStream)), (std::istreambuf_iterator<char>())); 
} 

Вызова для заполнения вектора не удается, выбрасывая исключение «плохого распределения».

Население иногда терпит неудачу при чтении первого файла; в других случаях он терпит неудачу на втором или третьем. Я использую Visual Studio 2010 и компилирую мой код как 32-разрядный, который должен иметь возможность адресовать до 2 ГБ. Я работаю на машине с 16 Гб оперативной памяти, с не менее 10 ГБ бесплатно, поэтому нехватка доступной памяти не является проблемой. Ошибка возникает как в конфигурации отладки, так и в выпуске.

Предварительная выделение памяти с помощью reserve не помогает.

Свойство max_size объекта возвращает 2^32, поэтому оно не является ограничением в контейнере.

Код в порядке с большим количеством небольших файлов с общим размером> 180 Мб, что заставляет меня думать, что мой код попадает в границу.

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

+0

Да, это вектор. – pdm2011

ответ

1

Вы делаете больше работы и выделяете больше памяти, чем вам нужно.

Сначала снимите указатель, он ничего не добавляет

vector<char> m_buffer; 

Затем вызвать изменение размера (не резервирует) до нужного размера

m_buffer.resize(streamLength); 

Если вы собираетесь запустить из памяти это когда его случится.

Наконец читать данные непосредственно в вектор, не используйте streambuf_iterator, что делает кто знает, что за кулисами

m_ifStream.read(&m_buffer[0], streamLength); 

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

+0

Следует ли изменить размер в целом на резерв? – pdm2011

+0

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

+0

Спасибо за ответы. Я использовал ваши предложения, но, к сожалению, эта же проблема возникает, на этот раз на четвертый вызов изменить размер. Мое подозрение - это куча коррупции где-то еще в коде, но я не нашел ничего инкриминирующего, и код надежный с большим количеством небольших файлов. Я использую сгенерированную оболочку для сортировки между C# и неуправляемым кодом, который может выполнять чрезмерное копирование контейнера - я попробую запустить его через профайлер, а также посмотреть, что из этого делает 64-битная сборка. – pdm2011

0
m_buffer = new vector<unsigned char>(); 
m_buffer->reserve(streamLength); 

//Next line sometimes results in bad_alloc when reading a large file 
*m_buffer = vector<unsigned char>((std::istreambuf_iterator<char>(m_ifStream)), (std::istreambuf_iterator<char>())); 

Первое, что поражает меня в том, что вы перезапись уже предварительно выделенную область в vector. Очевидно, нет смысла делать «резерв», если затем создать новый вектор для перезаписи этого вектора. Это просто означает, что у вас должно быть место для ОБОИХ этих относительно больших векторов.

Я бы начал с изменения m_buffer, чтобы не быть указателем на вектор - в этом случае вам не нужно звонить new vector<unsigned char> - он очень мало предназначен для указания указателя на вектор [в лучшем случае вы сохраняете 16 байты, если у вас есть вектор, который ничего не содержит].

Затем удалите reserve. Посмотрите, как это происходит.

+0

Это на самом деле то, что я делал изначально - я представил резерв, как я читал, что это может помочь. Я отредактировал вопрос, чтобы отразить это. – pdm2011

2

Если вы хотите, чтобы ваш звонок reserve() иметь никакого влияния на фактическое чтение, вы должны не создать временный std::vector<unsigned char> и присвоить этот временный к целевому вектору. Вместо этого you` использовать что-то вроде

m_buffer->assign(std::istreambuf_iterator<char>(m_ifStream), 
       std::istreambuf_iterator<char>()); 

Чтения файла без резервирования может фрагментировать память в некотором роде, но я не ожидал, что программа работает из памяти для небольшого файла, как ваши (файлы с парой пара GB может считаться большой, 160MB не очень большой). Если вы знаете размер файла, вы, вероятно, лучше от чтения файла с помощью read() члена, хотя:

m_buffer->resize(streamLength); 
m_ifStream.read(reinterpret_cast<char*>(m_buffer->data()), streamLength); 

Мое личное предположение, что std::bad_alloc исключения на самом деле являются результатом ошибки в определении размера файла. Например, я не думаю, что std::size_t обязательно достаточно большой, чтобы провести std::streamsize. Кроме того, нет попытки проверить, что любая из этих операций успешна, и если поток не может быть открыт, seekg() вернет pos_type(-1), который будет переведен в довольно большой std::size_t.

Смежные вопросы