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

dod*_*ong 54 c++ java file

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

apple
red
beautiful
smell
spark
input
Run Code Online (Sandbox Code Playgroud)

我需要编写一个程序来读取文件中的每个单词并打印单词计数.我使用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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

5e+08
Run time: 69.311 s
Run Code Online (Sandbox Code Playgroud)

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");
}
Run Code Online (Sandbox Code Playgroud)

输出:

5.0E8
Run time: 29 s
Run Code Online (Sandbox Code Playgroud)

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

lau*_*une 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快.

  • 为什么要使用double进行计数? (13认同)
  • +1为了性能而最终转入C语言. (2认同)

Jam*_*nze 7

语言处理I/O的方式存在许多显着差异,所有这些都可以以某种方式产生影响.

也许第一个(也是最重要的)问题是:如何在文本文件中编码数据.如果它是单字节字符(ISO 8859-1UTF-8),则Java必须 在处理之前将其转换为UTF-16 ; 根据区域设置,C++可能(或可能不)也转换或进行一些额外的检查.

正如已经指出的那样(部分地,至少),在C++中,>>使用特定于语言环境isspace,getline将简单地比较 '\n',这可能更快.(典型的实现 isspace将使用位图,这意味着每个字符的附加内存访问.)

优化级别和特定库实现也可能有所不同.在C++中,一个库实现的速度比另一个快2或3倍并不罕见.

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


Phi*_*ipp 5

我怀疑主要的不同之处在于java.io.BufferedReader它比std::ifstream缓冲更好,而ifsteam却没有.BufferedReader提前读取文件的大块,并在调用readLine()时将它们从RAM传递给程序,而std :: ifstream只有在通过调用>>-operator 提示时才读取几个字节.

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

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

  • `std :: ifstream`转发到`std :: filebuf`,它通常是缓冲区.(标准只需要一个字符缓冲区,但大多数实现将使用类似8K的东西.) (6认同)