処理に数時間かかる大きなファイルがあります。だから私はチャンクを推定し、並行してチャンクを読み取ろうと考えています。
単一のファイルを同時に読み取ることは可能ですか?私は両方を調べましたRandomAccessFile
がnio.FileChannel
、他の投稿に基づいて、このアプローチが機能するかどうかはわかりません。
処理に数時間かかる大きなファイルがあります。だから私はチャンクを推定し、並行してチャンクを読み取ろうと考えています。
単一のファイルを同時に読み取ることは可能ですか?私は両方を調べましたRandomAccessFile
がnio.FileChannel
、他の投稿に基づいて、このアプローチが機能するかどうかはわかりません。
ここで最も重要な質問は、あなたのケースのボトルネックは何かということです。
ボトルネックがディスク IOである場合、ソフトウェア部分でできることはあまりありません。異なる部分から同時にファイルを読み取ると、ディスクのパフォーマンスが低下するため、計算を並列化すると事態が悪化するだけです。
ボトルネックが処理能力であり、複数の CPU コアがある場合は、複数のスレッドを開始してファイルのさまざまな部分で作業することを利用できます。InputStream
複数の やを安全に作成しReader
て、ファイルのさまざまな部分を並行して読み取ることができます (開いているファイルの数に対するオペレーティング システムの制限を超えない限り)。次の例のように、作業をタスクに分割し、それらを並行して実行できます。
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
public class Split {
private File file;
public Split(File file) {
this.file = file;
}
// Processes the given portion of the file.
// Called simultaneously from several threads.
// Use your custom return type as needed, I used String just to give an example.
public String processPart(long start, long end)
throws Exception
{
InputStream is = new FileInputStream(file);
is.skip(start);
// do a computation using the input stream,
// checking that we don't read more than (end-start) bytes
System.out.println("Computing the part from " + start + " to " + end);
Thread.sleep(1000);
System.out.println("Finished the part from " + start + " to " + end);
is.close();
return "Some result";
}
// Creates a task that will process the given portion of the file,
// when executed.
public Callable<String> processPartTask(final long start, final long end) {
return new Callable<String>() {
public String call()
throws Exception
{
return processPart(start, end);
}
};
}
// Splits the computation into chunks of the given size,
// creates appropriate tasks and runs them using a
// given number of threads.
public void processAll(int noOfThreads, int chunkSize)
throws Exception
{
int count = (int)((file.length() + chunkSize - 1) / chunkSize);
java.util.List<Callable<String>> tasks = new ArrayList<Callable<String>>(count);
for(int i = 0; i < count; i++)
tasks.add(processPartTask(i * chunkSize, Math.min(file.length(), (i+1) * chunkSize)));
ExecutorService es = Executors.newFixedThreadPool(noOfThreads);
java.util.List<Future<String>> results = es.invokeAll(tasks);
es.shutdown();
// use the results for something
for(Future<String> result : results)
System.out.println(result.get());
}
public static void main(String argv[])
throws Exception
{
Split s = new Split(new File(argv[0]));
s.processAll(8, 1000);
}
}
複数の独立したスピンドルがある場合、大きなファイルの読み取りを並列化できます。たとえば、Raid 0 + 1 のストリップされたファイル システムがある場合、同じファイルへの複数の同時読み取りをトリガーすることで、パフォーマンスの向上を確認できます。
ただし、Raid 5 または 6 のような複合ファイル システム、またはプレーンな単一ディスクがある場合。ファイルを順番に読み取ることが、そのディスクから読み取る最速の方法である可能性が高くなります。注: OS は、ユーザーがシーケンシャルに読み取りを行っていることを認識すると、読み取りをプリフェッチするほどスマートであるため、追加のスレッドを使用してこれを行うことはほとんど役に立ちません。
つまり、複数のスレッドを使用しても、ディスクは速くなりません。
ディスクからより速く読み取りたい場合は、より高速なドライブを使用してください。一般的な SATA HDD は、約 60 MB/秒で読み取り、120 IOPS を実行できます。一般的な SATA SSD ドライブは、約 400 MB/秒で読み取り、80,000 IOPS を実行できます。一般的な PCI SSD は、900 MB/秒で読み取り、230,000 IOPS を実行できます。
ハード ドライブからファイルを読み取る場合、データを取得する最速の方法は、ファイルを最初から最後まで読み取ることです。つまり、同時にではありません。
処理に時間がかかる場合は、複数のスレッドで異なるデータのチャンクを同時に処理することでメリットが得られる可能性がありますが、それはファイルの読み取り方法とは関係ありません。
並列処理は可能ですが、ハードドライブは一度に1つのデータしか読み取ることができません。単一のスレッドでファイルを読み込んだ場合は、複数のスレッドでデータを処理できます。