development

C ++에서 빠른 텍스트 파일 읽기

big-blog 2020. 12. 12. 12:10
반응형

C ++에서 빠른 텍스트 파일 읽기


나는 현재 많은 큰 텍스트 파일을 읽는 것을 포함하는 C ++로 프로그램을 작성하고 있습니다. 각 줄에는 최대 400.000 줄이 있으며 극단적 인 경우 한 줄에 4000 자 이상이 있습니다. 테스트를 위해 ifstream과 cplusplus.com에서 제공하는 구현을 사용하여 파일 중 하나를 읽었습니다. 60 초 정도 걸렸는데 너무 길어요. 이제 나는 궁금해하고 있는데, 읽기 속도를 향상시킬 수있는 직접적인 방법이 있습니까?

편집 : 내가 사용하는 코드는 다소간입니다.

string tmpString;
ifstream txtFile(path);
if(txtFile.is_open())
{
    while(txtFile.good())
    {
        m_numLines++;
        getline(txtFile, tmpString);
    }
    txtFile.close();
}

편집 2 : 내가 읽은 파일은 82MB에 불과합니다. 나는 버퍼링을하기 위해 알아야 할 필요가 있을지도 모른다고 생각했기 때문에 4000에 도달 할 수 있다고 주로 말했다.

편집 3 : 답변 해 주셔서 감사합니다.하지만 내 문제를 감안할 때 개선 할 여지가 많지 않은 것 같습니다. 줄 수를 세고 싶기 때문에 readline을 사용해야합니다. ifstream을 바이너리로 인스턴스화해도 읽기 속도가 빨라지지 않았습니다. 가능한 한 많이 병렬화하려고 노력할 것입니다. 적어도 작동해야합니다.

편집 4 : 그래서 분명히 내가 할 수있는 몇 가지가 있습니다. 여기에 많은 시간을 투자 해주셔서 감사합니다. 감사합니다! =)


업데이트 : 초기 답변 아래에있는 (놀라운) 업데이트를 확인하십시오.


메모리 매핑 파일은 저에게 잘 봉사했습니다 1 :

#include <boost/iostreams/device/mapped_file.hpp> // for mmap
#include <algorithm>  // for std::find
#include <iostream>   // for std::cout
#include <cstring>

int main()
{
    boost::iostreams::mapped_file mmap("input.txt", boost::iostreams::mapped_file::readonly);
    auto f = mmap.const_data();
    auto l = f + mmap.size();

    uintmax_t m_numLines = 0;
    while (f && f!=l)
        if ((f = static_cast<const char*>(memchr(f, '\n', l-f))))
            m_numLines++, f++;

    std::cout << "m_numLines = " << m_numLines << "\n";
}

이것은 다소 빠르다.

최신 정보

이 접근 방식을 테스트하는 데 도움이되는 경우 Boost를 사용mmap 하는 대신 직접 사용 하는 버전 이 있습니다. Coliru에서 라이브로 확인하세요.

#include <algorithm>
#include <iostream>
#include <cstring>

// for mmap:
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

const char* map_file(const char* fname, size_t& length);

int main()
{
    size_t length;
    auto f = map_file("test.cpp", length);
    auto l = f + length;

    uintmax_t m_numLines = 0;
    while (f && f!=l)
        if ((f = static_cast<const char*>(memchr(f, '\n', l-f))))
            m_numLines++, f++;

    std::cout << "m_numLines = " << m_numLines << "\n";
}

void handle_error(const char* msg) {
    perror(msg); 
    exit(255);
}

const char* map_file(const char* fname, size_t& length)
{
    int fd = open(fname, O_RDONLY);
    if (fd == -1)
        handle_error("open");

    // obtain file size
    struct stat sb;
    if (fstat(fd, &sb) == -1)
        handle_error("fstat");

    length = sb.st_size;

    const char* addr = static_cast<const char*>(mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0u));
    if (addr == MAP_FAILED)
        handle_error("mmap");

    // TODO close fd at some point in time, call munmap(...)
    return addr;
}

최신 정보

GNU coreutils의 소스를 살펴봄으로써 내가 찾은 마지막 성능을 얻을 수있었습니다 wc. 놀랍게도 위의 메모리 매핑 파일 wc 을 사용하는 데 걸리는 시간의 약 84 % 에서 실행 된 다음 (매우 단순화 된) 코드를 사용하여 :

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;
}

1 여기에서 벤치 마크를 참조하십시오. C ++에서 공백으로 구분 된 부동 소수점을 빠르게 구문 분석하는 방법은 무엇입니까?


4000 * 400,000 = 1.6GB 하드 드라이브가 SSD가 아닌 경우 ~ 100MB / s의 순차 읽기를 얻을 수 있습니다. I / O에서 16 초입니다.

Since you don't elaborate on the specific code your using or how you need to parse these files (do you need to read it line by line, does the system have a lot of RAM could you read the whole file into a large RAM buffer and then parse it?) There's little you can do to speed up the process.

Memory mapped files won't offer any performance improvement when reading a file sequentially. Perhaps manually parsing large chunks for new lines rather than using "getline" would offer an improvement.

EDIT After doing some learning (thanks @sehe). Here's the memory mapped solution I would likely use.

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>

int main() {
    char* fName = "big.txt";
    //
    struct stat sb;
    long cntr = 0;
    int fd, lineLen;
    char *data;
    char *line;
    // map the file
    fd = open(fName, O_RDONLY);
    fstat(fd, &sb);
    //// int pageSize;
    //// pageSize = getpagesize();
    //// data = mmap((caddr_t)0, pageSize, PROT_READ, MAP_PRIVATE, fd, pageSize);
    data = mmap((caddr_t)0, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    line = data;
    // get lines
    while(cntr < sb.st_size) {
        lineLen = 0;
        line = data;
        // find the next line
        while(*data != '\n' && cntr < sb.st_size) {
            data++;
            cntr++;
            lineLen++;
        }
        /***** PROCESS LINE *****/
        // ... processLine(line, lineLen);
    }
    return 0;
}

Neil Kirk, unfortunately I can not reply to your comment (not enough reputation) but I did a performance test on ifstream an stringstream and the performance, reading a text file line by line, is exactly the same.

std::stringstream stream;
std::string line;
while(std::getline(stream, line)) {
}

This takes 1426ms on a 106MB file.

std::ifstream stream;
std::string line;
while(ifstream.good()) {
    getline(stream, line);
}

This takes 1433ms on the same file.

The following code is faster instead:

const int MAX_LENGTH = 524288;
char* line = new char[MAX_LENGTH];
while (iStream.getline(line, MAX_LENGTH) && strlen(line) > 0) {
}

This takes 884ms on the same file. It is just a little tricky since you have to set the maximum size of your buffer (i.e. maximum length for each line in the input file).


Do you have to read all files at the same time? (at the start of your application for example)

If you do, consider parallelizing the operation.

Either way, consider using binary streams, or unbffered read for blocks of data.


Use Random file access or use binary mode. for sequential, this is big but still it depends on what you are reading.


As someone with a little background in competitive programming, I can tell you: At least for simple things like integer parsing the main cost in C is locking the file streams (which is by default done for multi-threading). Use the unlocked_stdio versions instead (fgetc_unlocked(), fread_unlocked()). For C++, the common lore is to use std::ios::sync_with_stdio(false) but I don't know if it's as fast as unlocked_stdio.

For reference here is my standard integer parsing code. It's a lot faster than scanf, as I said mainly due to not locking the stream. For me it was as fast as the best hand-coded mmap or custom buffered versions I'd used previously, without the insane maintenance debt.

int readint(void)
{
        int n, c;
        n = getchar_unlocked() - '0';
        while ((c = getchar_unlocked()) > ' ')
                n = 10*n + c-'0';
        return n;
}

(Note: This one only works if there is precisely one non-digit character between any two integers).

And of course avoid memory allocation if possible...

참고URL : https://stackoverflow.com/questions/17925051/fast-textfile-reading-in-c

반응형