10yroの開発日記

福岡にある株式会社10yro(トイロ)のエンジニアが書いています

C# 並列処理(Task)でパフォーマンスを改善する

システム開発ではパフォーマンス(ユーザーが操作を行ってからシステムが応答するまでの時間)の改善というのは避けて通れない道です。
例えば、ある処理を直列で実行しており、約2秒掛かっていたとします。
それを1秒以内で終わらせることが必要になった場合、もし並列に実行しても問題ない処理であれば並列実行を試してみます。

今更なのですが、C#で直列パターンと並列パターンの処理時間を比較してみました。

直列パターン

以下は直列に実行するコードです。
単純に順番にメソッドを実行しています。

static void Main(string[] args)
{
    var sw = new Stopwatch();

    // 実行する処理のリスト
    var actions = new List<Action>()
    {
        () => HeavyMethod("処理1", 500000000),
        () => HeavyMethod("処理2", 300000000),
        () => HeavyMethod("処理3", 500000000),
        () => HeavyMethod("処理4", 100000000),
        () => HeavyMethod("処理5", 100000000),
        () => HeavyMethod("処理6", 200000000),
        () => HeavyMethod("処理7", 300000000),
        () => HeavyMethod("処理8", 400000000),
        () => HeavyMethod("処理9", 300000000),
        () => HeavyMethod("処理10", 100000000),
    };

    // すべての処理を直列で実行する
    Console.WriteLine($"★直列処理開始");
    sw.Start();
    foreach (var action in actions)
    {
        action.Invoke();
    }
    sw.Stop();

    Console.WriteLine($"★直列処理の時間 {sw.Elapsed.ToString()}");
    Console.ReadKey();
}

/// <summary>
/// ループするだけの処理
/// </summary>
/// <param name="memo">メモ</param>
/// <param name="maxCount">ループカウント</param>
private static void HeavyMethod(string memo, double maxCount)
{
    for (var i = 0; i < maxCount; i++) {}
    Console.WriteLine($"{memo} 完了");
}

実行結果

処理が完了するまで約2秒掛かりました。

並列パターン

Task.WaitAllを使って、直列で実行した同じメソッドを並列で実行してみます。

var taskList = new List<Task>();
foreach (var action in actions)
{
    taskList.Add(Task.Run(() => action.Invoke()));
}

// すべての処理を並列で実行する
Console.WriteLine($"★並列処理開始");
sw.Start();
Task.WaitAll(taskList.ToArray());
sw.Stop();
Console.WriteLine($"★並列処理の時間 {sw.Elapsed.ToString()}");
Console.ReadKey();

実行結果

1秒以内で完了しました!
これは最高ですね。

まとめ

一概に並列に変えたらパフォーマンスが上がるということはないかもしれませんが、1つの手としてやってみると良いかもしれません。
C#だとTaskを使えば簡単に実装できるので良いですね。