Как прочитать 4ГБ файл в 32-битной системе

В моем случае у меня есть разные файлы, давайте предположим, что у меня есть>4 ГБ файл с данными. Я хочу читать этот файл построчно и обрабатывать каждую строку. Одно из моих ограничений заключается в том, что софт должен работать на 32-битной MS Windows или на 64-битной с небольшим объемом оперативной памяти (минимум 4 ГБ). Вы также можете предположить, что обработка этих строк не является узким местом.

В текущем решении я прочитал этот файл ifstream и скопировать в какую-нибудь строку. Вот фрагмент, как это выглядит.

std::ifstream file(filename_xml.c_str());
uintmax_t m_numLines = 0;
std::string str;
while (std::getline(file, str))
{
    m_numLines++;
}

И хорошо, это работает, но медленно сейчас время для моих 3,6 ГБ данных:

real    1m4.155s
user    0m0.000s
sys     0m0.030s

Я ищу метод, который будет намного быстрее, чем, например, я обнаружил, что Как быстро анализировать поплавки в C++, разделенные пробелом? и мне очень понравилось представленное решение с boost::mapped_file, но я столкнулся с другой проблемой: что если мой файл слишком большой, а в моем случае файла 1 ГБ достаточно, чтобы отбросить весь процесс. Я должен заботиться о текущих данных в памяти, вероятно, люди, которые будут использовать этот инструмент, не имеют более 4 ГБ установленной оперативной памяти.

Так что я нашел этот mapped_file из boost, но как использовать его в моем случае? Можно ли частично прочитать этот файл и получить эти строки?

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

Спасибо,
Барт

4 ответа

Приятно видеть, что вы нашли мой тест на Как быстро разбирать разделенные пробелами в C++?

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

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

static uintmax_t wc(char const *fname)
{
    static const auto BUFFER_SIZE = 16*1024;
    int fd = open(fname, O_RDONLY);
    if(fd == -1)
        handle_error("open");

    /* Advise the kernel of our access pattern.  */
    posix_fadvise(fd, 0, 0, 1);  // FDADVICE_SEQUENTIAL

    char buf[BUFFER_SIZE + 1];
    uintmax_t lines = 0;

    while(size_t bytes_read = read(fd, buf, BUFFER_SIZE))
    {
        if(bytes_read == (size_t)-1)
            handle_error("read failed");
        if (!bytes_read)
            break;

        for(char *p = buf; (p = (char*) memchr(p, '\n', (buf + bytes_read) - p)); ++p)
            ++lines;
    }

    return lines;
}

В случае 64-битной системы с небольшой памятью вполне подойдет загрузка большого файла - все дело в адресном пространстве - хотя в этом случае она может быть медленнее, чем "самая быстрая" опция, она действительно зависит от того, что еще в памяти и сколько памяти доступно для отображения файла в. В 32-разрядной системе это не сработает, поскольку указатели на отображение файлов не будут превышать максимально 3,5 ГБ - и обычно около 2 ГБ - максимум - опять же, в зависимости от того, какие адреса памяти доступны для ОС для сопоставления файла.

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

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

Вы должны быть в состоянии сделать это, используя что-то вроде:

std::ifstream file(filename_xml.c_str());
char buffer[1024*1024];
file.rdbuf()->pubsetbuf(buffer, 1024*1024);

uintmax_t m_numLines = 0;
std::string str;
while (std::getline(file, str))
{
    m_numLines++;
}

Смотрите этот вопрос для получения дополнительной информации:

Как заставить IOStream работать лучше?

Поскольку это окна, вы можете использовать собственные функции файлов Windows с суффиксом "ex":

функции управления файлами Windows

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

Другие вопросы по тегам