2011-07-13 4 views
0

私は、外部uidを保存し、与えられたuidに設定されている別のID(アプリケーションの内部)を保存するHashMapを持っています。ディスクに保存されているHashMapのディスクからの読み込みが非常に遅い

例えば:

  • 123.345.432 = 00001
  • 123.354.433 = 00002

マップは同じ内部IDが使用されることを確認するために、UIDによってチェックされます。何かがアプリケーションに再送された場合。

次のように定義さDICOMUID2StudyIdentiferMap:我々が正常にロードする場合

private static Map DICOMUID2StudyIdentiferMap = Collections.synchronizedMap(new HashMap()); 

負荷は、しかし、それ以外の場合は、デフォルトの空のHashMapを使用しますが、それを上書きします。

そのを行うことによって、バックディスクから読み:

FileInputStream f = new FileInputStream(studyUIDFile); 
ObjectInputStream s = new ObjectInputStream(f); 

Map loadedMap = (Map)s.readObject(); 
DICOMUID2StudyIdentiferMap = Collections.synchronizedMap(loadedMap); 

をHashMapのは、使用してディスクに書き込まれます。

FileOutputStream f = new FileOutputStream(studyUIDFile); 
ObjectOutputStream s = new ObjectOutputStream(f); 

s.writeObject(DICOMUID2StudyIdentiferMap); 

私が持っている問題は、ローカルのEclipseのパフォーマンスで動作していることは結構です、ですが、ときアプリケーションは通常、HashMapがディスクからロードするのに数分かかるマシン上で実行されています。ロードされると、DICOMUID2StudyIdentiferMap.put(...、...)が値を返すかどうかを確認することによって、以前の値をチェックするのに時間がかかります。

どちらの場合でも、〜400kbのファイルで同じマップオブジェクトをロードします。 HashMapに含まれるHashMapには約3000個のキーと値のペアがあります。

なぜ1台のマシンでは非常に遅いですが、eclipseでは遅いですか?

マシンはXPを実行しているVMですが、最近ではHashMapの読み込みが遅くなっているので、そのサイズに関係する必要がありますが、400kbはそれほど大きくはありません。

何かアドバイス歓迎、TIA

+1

私の助言は、jvisualvmを使ってアプリケーションを攻撃して、その時間がどこで使われたかを調べることです。もう1つの選択肢は、同期化されたラッパーを削除して、状況が改善するかどうかを確認することです。 – biziclop

答えて

2

なく、あなたの地図を直列化することは最良の選択肢であることを確認してください。 Mapが永続性のためのディスクベースである場合、ディスク用に設計されたlibを使用しないでください。 Kyoto Cabinetをチェックしてください。実際にはC++で書かれていますが、Java APIがあります。私はそれを数回使用しました。使用するのが非常に簡単で、非常に高速で、巨大なサイズに拡大することができます。

これは私が東京キャビネット、京都の古いバージョンのためのコピー/貼り付けだが、それは基本的に同じだ例です。@biziclopコメントとして

import tokyocabinet.HDB; 

.... 

String dir = "/path/to/my/dir/"; 
HDB hash = new HDB(); 

// open the hash for read/write, create if does not exist on disk 
if (!hash.open(dir + "unigrams.tch", HDB.OWRITER | HDB.OCREAT)) { 
    throw new IOException("Unable to open " + dir + "unigrams.tch: " + hash.errmsg()); 
} 

// Add something to the hash 
hash.put("blah", "my string"); 

// Close it 
hash.close(); 
4

、あなたが使用して開始する必要がありますプロファイラを使用して、アプリケーションがすべての時間を費やしている場所を確認します。

これで結果が得られない場合は、いくつかの理論があります。

  • ヒープがなくなる可能性があります。JVMはヒープがなくなるにつれて、ほとんどすべての時間をガベージコレクションに費やすことができます。 GCロギングを有効にすると、これが表示されます。

  • ObjectInputStreamとObjectOutputStreamは、大量の小さな読み取りシステムコールを処理している可能性があります。バッファリングされたストリームでファイルストリームをラップして、それが顕著に高速化するかどうかを確認してください。

なぜそれが一台のマシン上ではなく、日食で非常に遅いのですか?

「完全なヒープ」理論はそれを説明することができます。 Eclipseのデフォルトのヒープサイズは、ヒープサイズオプションを持たないjava ...を使用して起動されたアプリケーションの場合よりもはるかに大きくなります。

+0

ありがとうございました。しかし私はリチャードHのソリューションを私の答えとしてマークしました。私は彼が提案した方法を使用するつもりだからです。 – james4563

1

おそらく、Mapのような類似した代替案を探してください。 SimpleDB、BerkeleyDB、Google BigTableのいずれかです。

1

Voldemortは、Linkedinの人気のあるオープンソースのKey-Valueストアです。彼らがどのようにしたのかを見るために、ソースコードを見ておいてください。今、私はhttps://github.com/voldemort/voldemort/blob/master/src/java/voldemort/serialization/ObjectSerializer.javaのシリアライズ部分を見ています。コードを見ると、彼らはByteArrayOutputStreamを使用しています。これは、ディスクを読み書きするためのより効率的な方法です。

なぜマシンでは非常に遅いのですか?

あなたの質問からはっきりと分かりませんが、VM(VirtualBox?)で実行されているEclipseですか?その場合は、完全なVMがディスクにアクセスするよりもはるかに高速であるため、メモリに格納されているため、処理が高速になる可能性があります。

1

これは、代替として使用できる122のNoSQLデータベースのリストです。

高価な操作が2つあります.1つはオブジェクトのシリアル化で、もう1つはディスクアクセスです。必要なデータを読み書きするだけでアクセスを高速化できます。あなたはカスタムフォーマットを使用してスピードアップすることができますseralization。

さらに効率的になるようにデータの構造を変更することもできます。次のアプローチを使用することをお勧めしますたびにマップ全体を再読み込み/書き換えたい場合。


private Map<Integer, Integer> mapping = new LinkedHashMap<Integer, Integer>(); 

public void saveTo(File file) throws IOException { 
    DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file))); 
    dos.writeInt(mapping.size()); 
    for (Map.Entry<Integer, Integer> entry : mapping.entrySet()) { 
     dos.writeInt(entry.getKey()); 
     dos.writeInt(entry.getValue()); 
    } 
    dos.close(); 
} 

public void loadFrom(File file) throws IOException { 
    DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(file))); 
    mapping.clear(); 
    int len = dis.readInt(); 
    for (int i = 0; i < len; i++) 
     mapping.put(dis.readInt(), dis.readInt()); 
    dis.close(); 
} 

public static void main(String[] args) throws IOException { 
    Random rand = new Random(); 
    Main main = new Main(); 
    for (int i = 1; i <= 3000; i++) { 
     // 100,000,000 to 999,999,999 
     int uid = 100000000 + rand.nextInt(900000000); 
     main.mapping.put(uid, i); 
    } 
    final File file = File.createTempFile("deleteme", "data"); 
    file.deleteOnExit(); 
    for (int i = 0; i < 10; i++) { 
     long start = System.nanoTime(); 
     main.saveTo(file); 
     long mid = System.nanoTime(); 
     new Main().loadFrom(file); 
     long end = System.nanoTime(); 
     System.out.printf("Took %.3f ms to save and %.3f ms to load %,d entries.%n", 
       (end - mid)/1e6, (mid - start)/1e6, main.mapping.size()); 
    } 
} 

プリント

Took 1.203 ms to save and 1.706 ms to load 3,000 entries. 
Took 1.209 ms to save and 1.203 ms to load 3,000 entries. 
Took 0.961 ms to save and 0.966 ms to load 3,000 entries. 

代わりに約10%高速であるTIntIntHashMap使用。 100万個のエントリに地図のサイズを大きく

は、私は、これはハッシュの問題かもしれないと思う

Took 412.718 ms to save and 62.009 ms to load 1,000,000 entries. 
Took 403.135 ms to save and 61.756 ms to load 1,000,000 entries. 
Took 399.431 ms to save and 61.816 ms to load 1,000,000 entries. 
1

を印刷します。 Mapで使用しているキーのタイプは何ですか?キーをうまく広げる効率的なhashCode()メソッドがありますか?

関連する問題