2

次のコードがあります。ここでは、キーと値のペアを格納するために HashMap (2 つの並列配列を使用) を使用しました (キーには複数の値を含めることができます)。ここで、将来の使用のために保存およびロードする必要があるため、ファイル チャネルを使用して保存およびロードします。このコードの問題点: 8 GB サーバーに約 1 億 2000 万のキーと値のペアを格納できます (実際には、8 GB のうち約 5 GB を JVM に割り当てることができ、これらの 2 つの並列配列には約 2.5 GB が必要です。私のコードのさまざまな処理にメモリが使用されます)。しかし、約 6 億/7 億のキーと値のペアを保存する必要があります。このコードを変更する方法を教えてください。これにより、6 億/7 億近くのキーと値のペアを保存できます。または、これに関するコメントは私にとっていいでしょう。別のポイントとして、ハッシュマップをメモリとの間で読み込んで保存する必要があります。ファイルチャネルを使用すると、少し時間がかかります。Stack Overflow のさまざまな提案によると、より高速なものは見つかりませんでした。私はObjectOutputStream、Zip出力ストリームも使用しましたが、以下のコードよりも遅くなります。とにかく、これらの2つの並列配列をそのような方法で保存することはできますか?ロード時間ははるかに高速になります. 以下のコードでテストケースを指定しました。それについてのコメントも私に役立ちます。

import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Arrays;
import java.util.Random;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.io.RandomAccessFile;

public class Test {

    public static void main(String args[]) {


        try {

            Random randomGenerator = new Random();

            LongIntParallelHashMultimap lph = new LongIntParallelHashMultimap(220000000, "xx.dat", "yy.dat");

            for (int i = 0; i < 110000000; i++) {
                lph.put(i, randomGenerator.nextInt(200000000));
            }

            lph.save();

            LongIntParallelHashMultimap lphN = new LongIntParallelHashMultimap(220000000, "xx.dat", "yy.dat");
            lphN.load();

            int tt[] = lphN.get(1);

            System.out.println(tt[0]);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class LongIntParallelHashMultimap {

    private static final long NULL = -1L;
    private final long[] keys;
    private final int[] values;
    private int size;
    private int savenum = 0;
    private String str1 = "";
    private String str2 = "";

    public LongIntParallelHashMultimap(int capacity, String st1, String st2) {
        keys = new long[capacity];
        values = new int[capacity];
        Arrays.fill(keys, NULL);
        savenum = capacity;
        str1 = st1;
        str2 = st2;
    }

    public void put(long key, int value) {
        int index = indexFor(key);
        while (keys[index] != NULL) {
            index = successor(index);
        }
        keys[index] = key;
        values[index] = value;
        ++size;
    }

    public int[] get(long key) {
        int index = indexFor(key);
        int count = countHits(key, index);
        int[] hits = new int[count];
        int hitIndex = 0;

        while (keys[index] != NULL) {
            if (keys[index] == key) {
                hits[hitIndex] = values[index];
                ++hitIndex;
            }
            index = successor(index);
        }

        return hits;
    }

    private int countHits(long key, int index) {
        int numHits = 0;
        while (keys[index] != NULL) {
            if (keys[index] == key) {
                ++numHits;
            }
            index = successor(index);
        }
        return numHits;
    }

    private int indexFor(long key) {
        return Math.abs((int) ((key * 5700357409661598721L) % keys.length));
    }

    private int successor(int index) {
        return (index + 1) % keys.length;
    }

    public int size() {
        return size;
    }

    public void load() {
        try {
            FileChannel channel2 = new RandomAccessFile(str1, "r").getChannel();
            MappedByteBuffer mbb2 = channel2.map(FileChannel.MapMode.READ_ONLY, 0, channel2.size());
            mbb2.order(ByteOrder.nativeOrder());
            assert mbb2.remaining() == savenum * 8;
            for (int i = 0; i < savenum; i++) {
                long l = mbb2.getLong();
                keys[i] = l;
            }
            channel2.close();

            FileChannel channel3 = new RandomAccessFile(str2, "r").getChannel();
            MappedByteBuffer mbb3 = channel3.map(FileChannel.MapMode.READ_ONLY, 0, channel3.size());
            mbb3.order(ByteOrder.nativeOrder());
            assert mbb3.remaining() == savenum * 4;
            for (int i = 0; i < savenum; i++) {
                int l1 = mbb3.getInt();
                values[i] = l1;
            }
            channel3.close();
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    public void save() {
        try {
            FileChannel channel = new RandomAccessFile(str1, "rw").getChannel();
            MappedByteBuffer mbb = channel.map(FileChannel.MapMode.READ_WRITE, 0, savenum * 8);
            mbb.order(ByteOrder.nativeOrder());

            for (int i = 0; i < savenum; i++) {
                mbb.putLong(keys[i]);
            }
            channel.close();

            FileChannel channel1 = new RandomAccessFile(str2, "rw").getChannel();
            MappedByteBuffer mbb1 = channel1.map(FileChannel.MapMode.READ_WRITE, 0, savenum * 4);
            mbb1.order(ByteOrder.nativeOrder());

            for (int i = 0; i < savenum; i++) {
                mbb1.putInt(values[i]);
            }
            channel1.close();
        } catch (Exception e) {
            System.out.println("IOException : " + e);
        }
    }
}
4

3 に答える 3

2

あなたが宣言したデータ型を考えると、これが可能であるとは思えません。プリミティブ型のサイズを掛けるだけです。

各行には、int を格納するために 4 バイト、long を格納するために 8 バイトが必要です。6 億行 * 1 行あたり 12 バイト = 7200 MB = 7.03 GB。JVM に 5 GB を割り当てることができるとします。したがって、すべてヒープで、このカスタム HashMap のみを格納したとしても、収まりません。関連するデータ型のサイズを縮小するか、RAM 以外の場所に格納することを検討してください。

于 2012-07-09T21:30:30.410 に答える
0

データベースをメモリではなくディスクに配置します。操作を書き直して、配列ではなくバッファで操作するようにします。次に、十分に大きなファイルを開き、マップされたバッファを使用して操作が必要な部分にアクセスできるようにします。最近マップされたいくつかのメモリ領域のキャッシュを実装すると、アプリケーションのパフォーマンスが向上するかどうかを試してみてください。これにより、共通領域を頻繁にマップおよびマップ解除する必要がなくなり、マップされたままにすることができます。

これにより、ディスクと RAM の両方の長所が得られるはずです。

  • データ構造の任意の部分へのランダムアクセスは簡単に実装できます
  • テーブルの頻繁に使用される部分へのアクセスはキャッシュされます
  • テーブルのめったに使用されない部分はメモリを占有しません

ご覧のとおり、これは局所性に大きく依存します。一部のキーが他のキーよりも一般的である場合、物事はうまく機能しますが、適切に分散されたキーはアクセスごとに新しいディスク操作を引き起こします. そのため、ほとんどのインメモリ ハッシュ マップには適切な分散が望ましいですが、よく使用されるキーを同様の場所にマップする他の構造は、ここではより適切に機能します。ただし、これらは衝突処理に干渉します。

于 2012-07-11T14:59:04.963 に答える
0

良い結果が得られる sqlite のようなメモリ内データベースを使用することをお勧めします。

于 2012-07-16T07:09:28.493 に答える