C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト


(過去ログ 176 を表示中)
■100957 / )  Re[1]: マルチthreadでファイルを読み込むメリットについて
□投稿者/ KOZ (361回)-(2022/12/05(Mon) 16:21:52)
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) { }
        }
    }
}

返信 編集キー/


管理者用

- Child Tree -