为什么Java比C ++更快地读取大文件?

时间:2014-04-09 07:04:17

标签: java c++ file

我有一个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 s\n", cost);

    return 0;
}

输出:

5e+08
Run time: 69.311 s

爪哇:

 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 ++的性能?

5 个答案:

答案 0 :(得分:64)

你没有比较同样的事情。 Java程序读取行,取决于换行符,而C ++程序读取空格分隔的“单词”,这是一个额外的工作。

尝试istream::getline

<强>后来

您也可以尝试执行基本读取操作来读取字节数组并对其进行扫描以获取换行符。

甚至更晚

在我的旧Linux笔记本上,与C ++ getline相比,jdk1.7.0_21并且不要告诉我这个4.3.3大概需要花费相同的时间。 (我们已经确定阅读单词较慢。)-O0和-O2之间没有太大区别,考虑到循环中代码的简单性,这并不让我感到惊讶。

最后一点 正如我所说,fin.read(缓冲区,LEN)LEN = 1MB并使用memchr扫描'\ n'导致另一个速度提高约20%,这使得C(现在还没有任何C ++)比Java快。

答案 1 :(得分:7)

在方式上存在许多显着差异 语言处理I/O,所有这些都可以通过一种方式发挥作用 或另一个。

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

正如已经指出的那样(至少部分地),在C ++中,>>使用 特定于区域设置的isspacegetline只会进行比较 '\n',这可能更快。 (典型的实现 isspace将使用位图,这意味着额外的内存 访问每个角色。)

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

最后,最重要的区别是:C ++的区别 文本文件和二进制文件之间。你打开了文件 文字模式;这意味着它将被“预处理” 最低级别,甚至在提取操作员看到它之前。这个 取决于平台:对于Unix平台,“预处理” 是无操作的;在Windows上,它会将CRLF对转换为'\n', 这将对性能产生一定的影响。如果我记得 Java期望正确(我已经多年没用过Java了) 更高级别的函数来处理这个,所以函数就像 readLine会稍微复杂一些。只是猜测 在这里,但我怀疑更高的额外逻辑 运行时的级别成本低于缓冲区预处理的级别 低等级。 (如果您在Windows下进行测试,可能会 尝试在C ++中以二进制模式打开文件。这个 应该对程序的行为没有任何影响 你使用>>;任何额外的CR都将被视为空白区域。同 getline,您必须添加逻辑以删除任何尾随 '\r'代码。)

答案 2 :(得分:5)

我怀疑主要的区别是java.io.BufferedReaderstd::ifstream表现更好,因为它缓冲,而ifsteam没有。 BufferedReader提前读取文件的大块并在调用readLine()时将它们从RAM传递给程序,而std :: ifstream一次只读取几个字节,当你通过调用{来提示它时{1}} - 操作者<​​/ p>

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

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

答案 3 :(得分:4)

我不是C ++的专家,但你至少有以下几点影响性能:

  1. 文件的操作系统级缓存
  2. 对于Java,您使用的是缓冲读取器,缓冲区大小默认为页面或其他内容。我不确定C ++流是如何做到的。
  3. 由于文件太大而JIT可能会被踢入,并且它可能比你没有为C ++编译器进行任何优化更好地编译Java字节代码。
  4. 由于I/O费用是这里的主要费用,我猜1和2是主要原因。

答案 4 :(得分:2)

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

在某种情况下,C ++不会比Java更快,但有时需要很多人才才能完成。但我不认为这个应该太难以击败,因为这是一项简单的任务。

File Mapping MSDN)中描述了适用于Windows的mmap。