0

外部uidを格納するHashMapがあり、特定のuidに設定されている別のID(アプリの内部)を格納します。

例えば:

  • 123.345.432 = 00001
  • 123.354.433 = 00002

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

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には、約3000のキーと値のペアがあります。

なぜ1台のマシンではそれほど遅いのに、Eclipseではそうではないのですか?

このマシンはXPを実行しているVMであり、HashMapの読み取りが遅くなり始めたばかりなので、そのサイズに関係している必要がありますが、400kbはそれほど大きくないと思います。

アドバイスを歓迎します、TIA

4

6 に答える 6

5

@biziclopがコメントしているように、プロファイラーを使用して、アプリケーションがすべての時間を費やしている場所を確認することから始める必要があります。

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

  • アプリケーションのヒープが不足しそうになっている可能性があります。JVMがヒープを使い果たしそうになると、ほぼすべての時間をガベージコレクションに費やして、無駄な試行を続ける可能性があります。これは、GCロギングを有効にした場合に表示されます。

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

なぜ1台のマシンではそれほど遅いのに、Eclipseではそうではないのですか?

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

于 2011-07-13T11:46:01.967 に答える
3

マップをシリアル化することが最良のオプションであるかどうかはわかりません。マップが永続性のためにディスクベースである場合、ディスク用に設計されたライブラリを使用してみませんか?京都内閣をご覧ください。実際にはC++で書かれていますが、JavaAPIがあります。私はそれを数回使用しました、それは非常に使いやすく、非常に速く、そして巨大なサイズに拡大縮小することができます。

これは、古いバージョンの京都である東京キャビネットのコピー/貼り付けの例ですが、基本的には同じです。

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();
于 2011-07-13T11:45:33.460 に答える
2

代わりに使用できる122のNoSQLデータベースのリストを次に示します。

ここでは2つのコストのかかる操作があります。1つはオブジェクトのシリアル化で、もう1つはディスクアクセスです。必要なデータの読み取り/書き込みのみでアクセスを高速化できます。カスタムフォーマットを使用することでスピードアップできるセラリゼーション。

データの構造を変更して、データをより効率的にすることもできます。マップ全体を毎回リロード/リライトしたい場合は、次のアプローチを使用することをお勧めします。


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.

代わりにTIntIntHashMapを使用すると、約10%高速になります。

マップのサイズを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.
于 2011-07-13T11:51:11.427 に答える
1

MapSimpleDB、BerkeleyDB、GoogleBigTableなどのように機能する代替手段を探す必要があるかもしれません。

于 2011-07-13T11:46:22.533 に答える
1

Voldemortは、Linkedinによる人気のあるオープンソースのKey-Valueストアです。ソースコードを見て、彼らがどのように行動したかを確認することをお勧めします。現在、 https://github.com/voldemort/voldemort/blob/master/src/java/voldemort/serialization/ObjectSerializer.javaでシリアル化の部分を見ています。彼らが使用しているコードを見ると、ディスクとの間で読み取り/書き込みを行うためのより効率的な方法であると私が思うByteArrayOutputStreamを使用しています。

なぜ1台のマシンではそれほど遅いのに、Eclipseではそうではないのですか?

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

于 2011-07-13T11:47:03.167 に答える
1

これはハッシュの問題かもしれないと思います。マップで使用しているキーのタイプは何ですか?また、キーを適切に分散する効率的なhashCode()メソッドがありますか?

于 2011-07-13T13:54:22.553 に答える