2022/12/06(Tue) 00:27:33 編集(投稿者)
■No100952 (弥生 さん) に返信
> OS問わず、一般的な原理からすれば、
> マルチthreadでハードディスクの中から多量なファイルを『同時に』メモリに読み込む場合、
> 必ず、シングルthreadで一つ一つのファイルを読み込む場合より、
> マクロ的(巨視的)に時間節約できると言えるのでしょうか。
どうしても待ち時間というのは発生してしまうので、シングルスレッドで
ハードウエアのパフォーマンスを 100% 引き出すことは難しいです。
うまく使えば時間節約できるでしょう。
C# でサンプルを書いてみました。
ディスクキャッシュが効いていると、マルチスレッドのほうが有利なので、
FILE_FLAG_NO_BUFFERING オプションを付けてキャッシュ無効で読み込んでます。
スレッド数やバッファ長などいろいろ変えて試してみてください。
ちょっと修正
・定数を先頭に
・スレッドで処理するファイル数の余りを均等に
・ファイル単位で確保した読み取りバッファをスレッド単位に
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
class Program
{
const int FILE_FLAG_NO_BUFFERING = 0x20000000;
const int MAX_THREADS = 10;
const int BUFFER_SIZE = 1024 * 1024 * 10; // 512 の整数倍でないとコケる
static void Main(string[] args) {
var targetDir = Environment.ExpandEnvironmentVariables(@"%WINDIR%\System32");
var files = Directory.GetFiles(targetDir);
Watch($"** Multi({MAX_THREADS}) **", () => { Multi(files, MAX_THREADS); });
Watch("** Single **", () => { Single(files); });
Console.ReadKey();
}
static void Watch(string message, Action action) {
var sw = new Stopwatch();
sw.Start();
action.Invoke();
sw.Stop();
Console.WriteLine($"{message} {sw.ElapsedMilliseconds} ms");
}
static void Multi(string[] files, int threads) {
int allCount = files.Length;
int splitCount = allCount / threads;
int remainder = allCount % threads;
int[] counts = Enumerable.Repeat(splitCount, threads).ToArray();
for (int i = 0; i < remainder; i++) {
counts[i] = splitCount + 1;
}
Task[] tasks = new Task[threads];
int position = 0;
for (int i = 0; i < threads; i++) {
int start = position;
int count = counts[i];
tasks[i] = Task.Factory.StartNew(() => { ReadFiles(files, start, count); });
position += count;
}
Task.WaitAll(tasks);
}
static void Single(string[] files) {
ReadFiles(files, 0, files.Length);
}
static void ReadFiles(string[] files, int start, int count) {
Console.WriteLine($"start={start} count={count}");
byte[] buffer = new byte[BUFFER_SIZE];
for (int i = 0; i < count; i++) {
ReadFile(files[start + i], buffer);
}
}
static void ReadFile(string file, byte[] buffer) {
FileOptions options = (FileOptions)FILE_FLAG_NO_BUFFERING;
using (var stream = new FileStream(file, FileMode.Open,
FileAccess.Read, FileShare.Read,
BUFFER_SIZE, options)) {
while (stream.Read(buffer, 0, BUFFER_SIZE) == BUFFER_SIZE) { }
}
}
}