为什么Java读取大文件的速度比C++快

Why does Java read a big file faster than C++?

本文关键字:速度 C++ 文件 Java 读取 为什么      更新时间:2023-10-16

我有一个2 GB文件(iputfile.txt),其中文件中的每一行都是一个单词,就像:

apple
red
beautiful
smell
spark
input

我需要写一个程序来读取文件中的每个单词并打印字数。我使用Java和C++编写了它,但结果令人惊讶:Java的运行速度是C++的2.3倍。我的代码如下:

C++:

int main() {
    struct timespec ts, te;
    double cost;
    clock_gettime(CLOCK_REALTIME, &ts);
    ifstream fin("inputfile.txt");
    string word;
    int count = 0;
    while(fin >> word) {
        count++;
    }
    cout << count << endl;
    clock_gettime(CLOCK_REALTIME, &te);
    cost = te.tv_sec - ts.tv_sec + (double)(te.tv_nsec-ts.tv_nsec)/NANO;
    printf("Run time: %-15.10f sn", cost);
    return 0;
}

输出:

5e+08
Run time: 69.311 s

Java:

 public static void main(String[] args) throws Exception {
    long startTime = System.currentTimeMillis();
    FileReader reader = new FileReader("inputfile.txt");
    BufferedReader br = new BufferedReader(reader);
    String str = null;
    int count = 0;
    while((str = br.readLine()) != null) {
        count++;
    }
    System.out.println(count);
    long endTime = System.currentTimeMillis();
    System.out.println("Run time : " + (endTime - startTime)/1000 + "s");
}

输出:

5.0E8
Run time: 29 s

在这种情况下,为什么Java比C++更快,我该如何提高C++的性能?

您不是在比较同一件事。Java程序读取行,依赖于换行符,而C++程序读取空白分隔的"单词",这是一项额外的工作。

尝试istream::getline

稍后

您还可以尝试执行一个基本的读取操作来读取字节数组,并扫描它以查找换行符。

甚至更晚

在我的旧Linux笔记本上,与C++getline相比,jdk1.7.0_21和don't tell-me-it’s old 4.3.3花费的时间大致相同。(我们已经确定单词的阅读速度较慢。)-O0和-O2之间没有太大区别,考虑到循环中代码的简单性,这并不让我感到惊讶。

最后一条注释正如我所建议的,LEN=1MB的fin.read(缓冲区,LEN)和使用memchr扫描"\n"会导致另一个大约20%的速度提高,这使得C(现在已经没有任何C++了)比Java更快。

语言处理I/O,所有这些都可以通过一种方式产生影响或其他。

也许第一个(也是最重要的)问题是:文本文件中编码的数据。如果是单字节字符(ISO 8859-1或UTF-8),则Java必须将其转换为UTF-16加工前;根据语言环境的不同,C++可能(也可能不)也可以转换或进行一些额外的检查。

正如已经指出的(至少部分地),在C++中,>>使用特定于区域设置的isspacegetline将简单地进行比较'n',这可能更快。(isspace将使用位图,这意味着额外的内存每个字符的访问权限。)

优化级别和特定的库实现可能也各不相同。一个库在C++中并不罕见实现比另一个快2或3倍。

最后,一个最显著的区别是:C++区分在文本文件和二进制文件之间。您已在中打开该文件文本模式;这意味着它将在最低级别,甚至在提取操作员看到它之前取决于平台:对于Unix平台,"预处理"是一个无操作;在Windows上将CRLF对转换为CCD_,这将对性能产生一定的影响。如果我还记得的话正确地(我已经好几年没有使用Java了),Java期望更高级别的函数来处理此问题,因此函数CCD_ 9将稍微复杂一些。只是猜测这里,但我怀疑级别在运行时的成本低于较低级别。(如果您在Windows下进行测试,您可能在C++中以二进制模式打开文件的实验。这当您使用>>;任何额外的CR都将被视为空白。具有getline,您必须添加逻辑以删除任何拖尾'r'添加到您的代码中。)

我怀疑主要的区别是java.io.BufferedReaderstd::ifstream表现更好,因为它具有缓冲能力,而ifsteam则没有。当您调用readLine()时,BufferedReader会提前读取文件的大块,并将它们从RAM交给您的程序,而当您通过调用>>运算符提示std::ifstream时,它一次只读取几个字节。

从硬盘驱动器顺序访问大量数据通常比一次访问多个小块要快得多。

一个更公平的比较是将std::ifstream与未缓冲的java.io.FileReader.进行比较

我不是C++专家,但至少有以下因素会影响性能:

  1. 文件的操作系统级缓存
  2. 对于Java,您使用的是缓冲读取器,缓冲区大小默认为页面或其他内容。我不确定C++流是如何做到这一点的
  3. 由于文件太大,JIT可能会被启动,而且它可能会比不为C++编译器打开任何优化时更好地编译Java字节码

由于I/O成本是这里的主要成本,我想1和2是主要原因。

我也会尝试使用mmap而不是标准的文件读/写。这应该让您的操作系统处理读写操作,而您的应用程序只关心数据。

在任何情况下,C++都不可能比Java快,但有时它需要非常有才华的人做大量的工作。但我认为这一点不应该太难击败,因为这是一项简单的任务。

文件映射(MSDN)中介绍了适用于Windows的mmap。