簡體   English   中英

為什么Java比C ++更快地讀取大文件?

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

我有一個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

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和不告訴我它的舊版本4.3.3幾乎同時進行。 (我們已經確定讀取單詞較慢。)-O0和-O2之間沒有太大區別,考慮到循環中代碼的簡單性,這並不讓我感到驚訝。

最后一點注意事項正如我所說,fin.read(緩沖區,LEN)LEN = 1MB並使用memchr掃描'\\ n'導致另一個速度提高約20%,這使得C(沒有任何C ++留下現在)比Java快。

語言處理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' 。)

我懷疑主要區別在於java.io.BufferedReaderstd::ifstream表現更好,因為它緩沖,而ifsteam則沒有。 BufferedReader提前讀取文件的大塊,並在調用readLine()時將它們從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。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM