1. ホーム
  2. c++

c++による高速なテキストファイルの読み込み

2023-10-08 23:02:29

質問

私は現在、たくさんの大きなテキストファイルを読むことを含むプログラムをc++で書いています。それぞれは、極端な場合、1行あたり4000文字以上で、~400.000行あります。テストのために、ifstream と cplusplus.com によって提供される実装を使用して、ファイルの 1 つを読みました。その結果、約60秒かかりましたが、これはあまりにも長すぎます。今思ったのですが、読み取り速度を向上させる簡単な方法はあるのでしょうか?

を編集しています。 私が使用しているコードは多かれ少なかれこれです。

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

edit 2: 私が読み込んだファイルは82MBしかありません。私が主に4000に達する可能性があると言ったのは、バッファリングを行うために知る必要があるかもしれないと思ったからです。

edit 3: ご回答ありがとうございました。行数をカウントしたいので、readlineを使用しなければなりません。ifstreamをバイナリとしてインスタンス化しても、読み込みは速くなりませんでした。出来るだけ並列化するようにします、それくらいはうまくいくはずです。

edit 4: どうやら私にできることがいくつかあるようです。

これのために多くの時間を費やしてくれたseheに感謝します、私はそれに非常に感謝します! =)

どのように解決するのですか?

更新情報です。 最初の答えの下にある(驚くべき)更新を必ず確認してください。


メモリ マップされたファイルは私にとても役に立ちました。 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";
}

これは割とすぐ終わるはずです。

更新

この方法をテストするのに役立つかもしれないので、以下はそのバージョンです。 を使って mmap をBoostを使わず直接使用します。 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++でスペースで区切られた浮動小数点を素早くパースする方法は?