web-dev-qa-db-ja.com

なぜJavaはC ++よりも大きなファイルを高速に読み取るのですか?

2 GBのファイルがあります(iputfile.txt)ここで、ファイルのすべての行は次のようにWordです。

Apple
red
beautiful
smell
spark
input

ファイル内のすべてのWordを読み取り、Wordカウントを出力するプログラムを作成する必要があります。 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++のパフォーマンスを向上させるにはどうすればよいですか。

54
dodolong

あなたは同じものを比較していません。 Javaプログラムは改行に依存して行を読み取り、C++プログラムは空白で区切られた「単語」を読み取ります。これは少し余分な作業です。

istream::getlineをお試しください。

後で

また、基本的な読み取り操作を実行してバイト配列を読み取り、改行をスキャンすることもできます。

後でさえ

私の古いLinuxノートブックでは、jdk1.7.0_21とnot-tell-me-it's-old 4.3.3は、C++ getlineと比較すると、ほぼ同じ時間がかかります。 (単語の読み取りが遅いことがわかっています。)-O0と-O2の間に大きな違いはありませんが、ループ内のコードが単純であることを考えると、驚くことではありません。

最後の注意私が示唆したように、LEN = 1MBでfin.read(buffer、LEN)を使用し、memchrを使用して「\ n」をスキャンすると、速度が約20%向上し、C( Javaよりも高速なC++は今のところ残っていません。

64
laune

言語の処理方法にはいくつかの重要な違いがあります I/O 。これらはすべて、何らかの方法で違いを生む可能性があります。

おそらく最初の(そして最も重要な)問題は、データがテキストファイルにどのようにエンコードされているかです。シングルバイト文字( ISO 8859-1 または TF-8 )の場合、Javaは-に変換する必要があります TF-16 処理前;ロケールに応じて、C++は変換または追加チェックを行う場合と行わない場合があります。

指摘されているように(少なくとも、少なくとも)、C++では、>>はロケール固有のisspaceを使用し、getline'\n'を単純に比較します。もっと早く。 (isspaceの一般的な実装はビットマップを使用します。これは、各文字に対する追加のメモリアクセスを意味します。)

最適化レベルと特定のライブラリの実装も異なる場合があります。 C++では、あるライブラリの実装が別のライブラリの実装の2倍または3倍高速であることは珍しくありません。

最後に、最も重要な違い:C++はテキストファイルとバイナリファイルを区別します。ファイルをテキストモードで開きました。つまり、抽出オペレーターでさえも確認される前に、最低レベルで「前処理」されます。これはプラットフォームによって異なります。Unixプラットフォームの場合、「前処理」は何もしません。 Windowsでは、CRLFペアを'\n'に変換します。これは、パフォーマンスに明確な影響を与えます。正しく思い出せば(Javaを何年も使用していません)、Javaはこれを処理するために高レベルの関数を想定しているため、readLineは少し複雑になります。ここで推測するだけですが、高レベルの追加ロジックの方が低レベルのバッファ前処理よりも実行時のコストが少ないと思います(Windowsでテストしている場合は、 C++ではバイナリモードのファイルです。>>を使用しても、プログラムの動作に違いはありません。余分なCRは空白と見なされます。getlineを使用すると、コードの末尾の'\r'を削除するロジック。)

7
James Kanze

主な違いは、バッファがバッファリングするため、_Java.io.BufferedReader_は_std::ifstream_よりもパフォーマンスが良いのに対し、ifsteamはバッファリングしないためだと思います。 BufferedReaderは、ファイルの大きなチャンクを事前に読み取り、readLine()を呼び出すと、RAM)からプログラムに渡しますが、std :: ifstreamは、 _>>_- operatorを呼び出してプロンプトを表示するとき。

通常、ハードドライブから大量のデータに順次アクセスする方が、一度に多数の小さなチャンクにアクセスするよりもはるかに高速です。

より公平な比較は、std :: ifstreamとバッファリングされていないJava.io.FileReaderを比較することです。

5
Philipp

私はC++の専門家ではありませんが、パフォーマンスに影響を与えるには少なくとも次のことが必要です。

  1. ファイルのOSレベルのキャッシュ
  2. Javaの場合、バッファ付きリーダーを使用しており、バッファサイズはデフォルトでページなどに設定されています。C++ストリームがこれをどのように行うかはわかりません。
  3. ファイルは非常に大きいので、JITが実行される可能性が高く、C++コンパイラの最適化をオンにしない場合よりもJavaバイトコードがコンパイルされる可能性があります。

ここでは I/O コストが主要なコストであるため、1と2が主要な理由だと思います。

4
Alex Suo

また、標準ファイルの読み取り/書き込みの代わりに mmap を使用してみます。これにより、アプリケーションがデータのみに関係しているときに、OSが読み取りと書き込みを処理できるようになります。

C++がJavaよりも高速であるという状況はありませんが、非常に才能のある人から多くの作業が必要になる場合があります。しかし、これは簡単な作業なので、これを打ち負かすのが難しいとは思わない。

windowsのmmapについては、File Mapping[〜#〜 ] msdn [〜#〜] )。

2
rich remer