2

ファイルの内容を使用してマップを作成しようとすると、コードは次のようになります。

    System.out.println("begin to build the sns map....");
    String basePath = PropertyReader.getProp("oldbasepath");
    String pathname = basePath + "\\user_sns.txt";
    FileReader fr;
    Map<Integer, List<Integer>> snsMap = 
            new HashMap<Integer, List<Integer>>(2000000);
    try {
        fr = new FileReader(pathname);
        BufferedReader br = new BufferedReader(fr);
        String line; 
        int i = 1;
        while ((line = br.readLine()) != null) {
            System.out.println("line number: " + i);
            i++;

            String[] strs = line.split("\t");
            int key = Integer.parseInt(strs[0]);
            int value = Integer.parseInt(strs[1]);
            List<Integer> list = snsMap.get(key);
            //if the follower is not in the map
            if(snsMap.get(key) == null) 
                list = new LinkedList<Integer>();
            list.add(value);
            snsMap.put(key, list);
            System.out.println("map size: " + snsMap.size());
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    System.out.println("finish building the sns map....");
    return snsMap;

プログラムは最初は非常に高速ですが、印刷される情報が次のようになると非常に遅くなります。

 map size: 1138338
 line number: 30923602
 map size: 1138338
 line number: 30923603 
 ....

Javaプロファイラーの代わりにBufferedReaderとHashMapのパフォーマンスを判断するために、2つのSystem.out.println()句を使用して推論しようとしています。行番号情報を取得してから地図サイズの情報を取得するのに時間がかかる場合もあれば、地図サイズを取得してから行番号情報の情報を取得するのに時間がかかる場合もあります。私の質問は:私のプログラムを遅くするのはどれですか?大きなファイルの場合はBufferedReader、大きなマップの場合はHashMap?

4

5 に答える 5

3

Eclipseの内部からこれをテストしている場合、Eclipseがコンソールビューでその出力をキャプチャするため、stdout/stderrへの書き込みのパフォーマンスが大幅に低下することに注意する必要があります。タイトループ内での印刷は、Eclipseの外部であっても、常にパフォーマンスの問題です。

しかし、あなたが不満を言っているのが3000万行を処理した後に経験した速度低下であるなら、それはメモリの問題だと思います。最初は激しいGCのために速度が低下し、次に。で壊れOutOfMemoryErrorます。

于 2012-05-17T11:10:26.553 に答える
2

プログラムが遅い理由を理解するには、いくつかのプロファイリングツールを使用してプログラムをチェックする必要があります。一般に、ファイルへのアクセスはメモリ操作よりもはるかに遅いため(メモリに制約があり、過剰なGCを実行している場合を除く)、ここではファイルの読み取りが遅くなる可能性があります。

于 2012-05-17T11:07:40.627 に答える
2

プロファイルを作成する前に、何が遅いのか、何が遅いのかがわかりません。

ほとんどの場合、System.outがボトルネックとして表示されるため、それらを使用せずに再度プロファイルを作成する必要があります。パフォーマンスのボトルネックを見つけるためにできる最悪のことです。そうすることで、通常、さらに悪いボトルネックが追加されるためですSystem.out

コードの明らかな最適化は、行を移動することです

snsMap.put(key, list);

if ステートメントに。これは、新しいリストを作成したときにのみ入力する必要があります。それ以外の場合、putは現在の値をそれ自体に置き換えるだけです。

オブジェクトに関連するJavaコストInteger(特にJavaコレクションAPIでの整数の使用)は、主にメモリ(したがってガベージコレクション!)の問題です。効率的に使用するためにコードをどれだけうまく調整できるかに応じて、GNUtroveなどのプリミティブコレクションを使用することで大幅な向上が得られる場合があります。Troveの利点のほとんどは、メモリ使用量にあります。TIntArrayList間違いなく、TIntObjectMapGNUトローヴから使用するコードを書き直してみてください。特にプリミティブ型の場合は、リンクリストも避けたいと思います。

大まかに見積もると、HashMap<Integer, List<Integer>>エントリごとに少なくとも3*16バイトが必要です。二重リンクリストでも、保存されているエントリごとに少なくとも2*16バイトが必要です。1mキー+30m値〜1GB。オーバーヘッドはまだ含まれていません。GNUトローブTIntObjectHash<TIntArrayList>では、キーごとに4 + 4 + 16バイト、値ごとに4バイト、つまり144MBである必要があります。オーバーヘッドはおそらく両方で似ています。

Troveが使用するメモリが少ない理由は、型がなどのプリミティブ値に特化しているためですint。それらはint値を直接格納するため、それぞれを格納するために4バイトを使用します。

JavaコレクションHashMapは多くのオブジェクトで構成されています。大まかに次のようにEntryなります。それぞれキーオブジェクトと値オブジェクトを指すオブジェクトがあります。Javaでジェネリックスを処理する方法のため、これらはオブジェクトである必要があります。あなたの場合、キーIntegerは16バイト(4バイトのマーク、4バイトのタイプ、4バイトの実際のint値、4バイトのパディング)AFAIKを使用するオブジェクトになります。これらはすべて32ビットシステムの見積もりです。したがって、の1つのエントリには、HashMapおそらく16(エントリ)+ 16(整数キー)+ 32(まだ空のLinkedList)バイトのメモリが必要であり、これらはすべてガベージコレクションで考慮する必要があります。

Integerオブジェクトがたくさんある場合は、プリミティブを使用してすべてを保存できる場合の4倍のメモリが必要になりますintこれは、Javaで実現されたクリーンなOOP原則に支払うコストです。

于 2012-05-17T11:32:45.373 に答える
0

最良の方法は、プロファイラー(JProfileなど)を使用してプログラムを実行し、どの部分が遅いかを確認することです。また、デバッグ出力は、たとえば、プログラムの速度を低下させる可能性があります。

于 2012-05-17T11:07:40.863 に答える
0

ハッシュマップは遅くはありませんが、実際にはマップの中で最速です。HashTableは、マップ間で安全な唯一のスレッドであり、低速になる場合があります。

重要な注意:データを読み取った後、BufferedReaderとファイルを閉じてください...これは役立つ場合があります。

例:br.close()file.close()

タスクマネージャからシステムプロセスを確認してください。バックグラウンドで実行されているプロセスもある可能性があります。

日食は実際のリソースを大量に消費することがあるため、コンソールから実行して確認してください。

于 2012-05-17T11:11:48.927 に答える