Java で wordcount プログラムを実装しました。基本的に、プログラムは大きなファイル (私のテストでは、数字のみを含む 10 GB のデータ ファイルを使用しました) を取り、各「単語」が出現する回数をカウントします。この場合、数字 (たとえば 23723ファイル内で 243 回)。
以下は私の実装です。主にパフォーマンスを念頭に置いて改善しようとしていますが、他のいくつかのことも同様に改善しており、いくつかのガイダンスを探しています. 修正したい問題のいくつかを次に示します。
現在、プログラムはスレッド化されており、適切に動作します。ただし、私が行っているのは、メモリのチャンク
(500MB/NUM_THREADS)
を各スレッドに渡すことであり、各スレッドは wordcount に進みます。ここでの問題は、各スレッドにさらにデータを渡す前に、すべてのスレッドが完了するのをメインスレッドに待機させることです。それほど大きな問題ではありませんが、いくつかのスレッドが待機してしばらくの間何もしない期間があります。ある種のワーカー プールまたはエグゼキューター サービスでこの問題を解決できると思います (この構文についてはまだ学習していません)。このプログラムは、整数を含むファイルに対してのみ機能します。それは問題だ。未使用の変数を大量に作成せずにデータを反復処理する方法がわからなかったため、これにかなり苦労しました(Stringを使用するか、StringBuilderを使用してもパフォーマンスが大幅に低下しました)。現在、入力が整数であることを知っているという事実を使用し、一時変数を として保存するだけな
int
ので、メモリの問題はありません。区切り文字がスペースであろうと複数の文字であろうと、ある種の区切り文字を使用できるようにしたいと考えています。グローバルな ConcurrentHashMap を使用して、キーと値のペアをストーリー化しています。たとえば、スレッドが「24624」という数字を見つけた場合、マップ内でその数字を検索します。存在する場合、そのキーの値を 1 増やします。末尾のキーの値は、そのキーの出現回数を表します。それで、これは適切な設計ですか?各スレッドに独自のハッシュマップを与え、最後にそれらをすべてマージすると、パフォーマンスが向上しますか?
クラス RandomAccessMemory を使用せずに、オフセットを使用してファイルをシークする他の方法はありますか? このクラスはバイト配列にのみ読み込まれるため、変換する必要があります。この変換のタイミングは計っていませんが、別のものを使用した方が速いかもしれません。
私は他の可能性にもオープンです。これはまさに頭に浮かぶものです。
注: ファイルを分割することは、検討したいオプションではありません。これは、独自のファイルを作成するべきではないサーバーに展開する可能性があるためですが、実際にパフォーマンスが向上する場合は、聞くかもしれません.
その他の注意: 私は Java スレッドと StackOverflow が初めてです。穏やかな。
public class BigCount2 {
public static void main(String[] args) throws IOException, InterruptedException {
int num, counter;
long i, j;
String delimiterString = " ";
ArrayList<Character> delim = new ArrayList<Character>();
for (char c : delimiterString.toCharArray()) {
delim.add(c);
}
int counter2 = 0;
num = Integer.parseInt(args[0]);
int bytesToRead = 1024 * 1024 * 1024 / 2; //500 MB, size of loop
int remainder = bytesToRead % num;
int k = 0;
bytesToRead = bytesToRead - remainder;
int byr = bytesToRead / num;
String filepath = "C:/Users/Daniel/Desktop/int-dataset-10g.dat";
RandomAccessFile file = new RandomAccessFile(filepath, "r");
Thread[] t = new Thread [num];//array of threads
ConcurrentMap<Integer, Integer> wordCountMap = new ConcurrentHashMap<Integer, Integer>(25000);
byte [] byteArray = new byte [byr]; //allocates 500mb to a 2D byte array
char[] newbyte;
for (i = 0; i < file.length(); i += bytesToRead) {
counter = 0;
for (j = 0; j < bytesToRead; j += byr) {
file.seek(i + j);
file.read(byteArray, 0, byr);
newbyte = new String(byteArray).toCharArray();
t[counter] = new Thread(
new BigCountThread2(counter,
newbyte,
delim,
wordCountMap));//giving each thread t[i] different file fileReader[i]
t[counter].start();
counter++;
newbyte = null;
}
for (k = 0; k < num; k++){
t[k].join(); //main thread continues after ALL threads have finished.
}
counter2++;
System.gc();
}
file.close();
System.exit(0);
}
}
class BigCountThread2 implements Runnable {
private final ConcurrentMap<Integer, Integer> wordCountMap;
char [] newbyte;
private ArrayList<Character> delim;
private int threadId; //use for later
BigCountThread2(int tid,
char[] newbyte,
ArrayList<Character> delim,
ConcurrentMap<Integer, Integer> wordCountMap) {
this.delim = delim;
threadId = tid;
this.wordCountMap = wordCountMap;
this.newbyte = newbyte;
}
public void run() {
int intCheck = 0;
int counter = 0; int i = 0; Integer check; int j =0; int temp = 0; int intbuilder = 0;
for (i = 0; i < newbyte.length; i++) {
intCheck = Character.getNumericValue(newbyte[i]);
if (newbyte[i] == ' ' || intCheck == -1) { //once a delimiter is found, the current tempArray needs to be added to the MAP
check = wordCountMap.putIfAbsent(intbuilder, 1);
if (check != null) { //if returns null, then it is the first instance
wordCountMap.put(intbuilder, wordCountMap.get(intbuilder) + 1);
}
intbuilder = 0;
}
else {
intbuilder = (intbuilder * 10) + intCheck;
counter++;
}
}
}
}