C++中的ifstream.getline()比Java中的BufferedReader.readLine()慢很多吗?

4

我正在重写我的一个Android应用程序,以利用NDK。每次运行该应用程序时,其中一项首要任务是打开一个1.5MB的文本文件(大约150k行),并将每一行放入数据结构中。使用Java的BufferedReader.readLine()方法进行此操作时,从SD卡读取文件需要大约2.5秒钟。以下是我用于此操作的代码:

try {
    BufferedReader br = new BufferedReader(new FileReader("/sdcard/testfile.txt"));
    String thisLine;
    while ((thisLine = br.readLine()) != null) {
        Log.d(TAG, thisLine);
    }
} catch (IOException e) {
    //Log error
}

使用C++和ifstream读取同一个文件需要更长时间,大约需要3分钟。以下是我在C++中使用的代码:

char buffer[256];
ifstream ifs;
ifs.open("/sdcard/testfile.txt", ifstream::in);
if (ifs.is_open()) {
    while (!ifs.eof()) {
        ifs.getline (buffer,100);
        LOGD(buffer);
    }
}

我对C++已经有些生疏了,但是我想不出任何逻辑上的解释来说明为什么读取时间会增加。 一度我以为可能是LOGD函数的问题,但是我尝试完全将其删除后,读取时间并没有得到很大的改善。 有人对此有什么想法吗?在C++中有更快的逐行读取文件的方法吗?谢谢。


5
ifstream ifs.open(...) 无法编译通过... - sth
我注意到你打了android的标签,虽然我不是android专家,但是c++代码是否可能会回调“java”代码来执行I/O操作呢? - jcoder
@sth:抱歉,我试图从记忆中发布这个。已经在原始帖子中修复了。 - Eric
@JohnB:它绝对不会回调Java做任何事情。 - Eric
@某人:这种事情时有发生。我在 Stack Overflow 上也曾多次因阅读理解失败而失败。8v) - Fred Larson
显示剩余5条评论
3个回答

14

有一种想法是 stdio 同步可能会拖慢你的速度。可以关闭它。我不知道这是否可以解释所有的差异,但你可以尝试。此外,你没有正确使用 eof()。最后,我建议使用 getline() 的 std::string 版本。

std::ios::sync_with_stdio(false);
ifstream ifs("/sdcard/testfile.txt");
std::string line;
while (getline(ifs, line))
{
    LOGD(line);
}

我没有测试过这段代码,但你可以尝试一下,看看是否有改进。


谢谢回复,但这似乎没有产生任何影响。 - Eric
说话太早了...这个解决了。现在它以大致与Java版本相同的时间读取文件。非常感谢! - Eric
太好了,我刚刚在我的程序中尝试了ios::sync_with_stdio(false),它将getline的速度提高了7倍,感谢这个绝妙的提示。 - NoxArt

4

这个流是否可能是无缓冲的,每个字节的数据都需要进行SD访问?为了提供缓冲区,请按照以下步骤操作(大小根据您的需要确定)。

ifstream ifs;
char stream_buffer[4096];
ifs.rdbuf()->pubsetbuf(stream_buffer, sizeof(stream_buffer) );
ifs.open(argv[1]);

这对我来说听起来非常有希望,但似乎并没有帮助。无论如何,谢谢你的回复! - Eric

0

C++不会为您缓冲流(编辑:默认情况下它们不会,参见Dave Smith的解决方案)。我会告诉您,在普通盘片式磁盘上,您的代码将变得很慢。就我个人而言,我对Android没有太多经验。

通常我会使用类似这样的东西:

struct buffered_reader {
    buffered_reader(std::istream &data_) : data(data_), done(false) {}
    bool next(std::string &line) {
        if (!lines.size()) {
            if (done)
                return false;
            std::string line;
            for (size_t i = 0; i < 500; i++) {
                std::getline(data, line);
                if (data.eof()) {
                    done = true;
                    break;
                }
                lines.push_back(line);
            }
        }
        line = lines.front();
        lines.pop_front();
        return true;
    }
    std::istream &data;
    bool done;

    std::deque<std::string> lines;
};

TEST(blah) {
    std::stringstream ss;
    ss << "a" << std::endl;
    ss << "a" << std::endl;
    ss << "a" << std::endl;
    ss << "a" << std::endl;

    buffered_reader reader(ss);
    std::string line;
    while(reader.next(line)) {
        std::cout << line << std::endl;
    }
}

这个还没有在任何生产环境中使用,所以除了你在这里看到的测试之外,没有其他保证 ;)


1
你说C++不缓冲流,但文档似乎表明相反。所有的ifstream对象似乎都有一个关联的filebuf对象,它们用于缓冲所有输入。我理解错了吗?感谢回复。来源:cplusplus.com - Eric
@Eric 啊,在我的脑海中读到的是“C++默认情况下不为您缓冲流”,最后一部分丢失了。使用Dave Smith的解决方案完全没问题,我只是倾向于使用/需要基于行的缓冲。我怀疑摊销时间是等效的。 - Tom Kerr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接