【C#】論理プロセッサ数を取得して並列処理を最適化する

目次

概要

実行環境のマシンが搭載している論理プロセッサ(論理コア)の数を取得する方法です。 Parallel.ForEach や PLINQ などで並列処理を行う際、ハードウェアのリソースに合わせて並列度(スレッド数)を最適化するために利用されます。

仕様(入出力)

  • 入力
    • なし(実行環境のハードウェア情報を参照)
  • 出力
    • 現在のプロセスで使用可能な論理プロセッサ数(int
  • 定義
    • ハイパースレッディングなどの技術が有効な場合、物理コア数ではなく、OSが認識している論理スレッド数が返されます。

基本の使い方

Environment.ProcessorCount プロパティを参照するだけで、整数値として取得できます。

// 4コア8スレッドのCPUなら "8" が返る
int logicalCores = Environment.ProcessorCount;
Console.WriteLine($"搭載コア数: {logicalCores}");

コード全文

取得したプロセッサ数を利用して、並列処理の同時実行数(並列度)を動的に設定するコンソールアプリケーションの例です。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        // 1. 論理プロセッサ数の取得
        int processorCount = Environment.ProcessorCount;
        
        Console.WriteLine($"[System Info] 論理プロセッサ数: {processorCount}");

        // 2. 並列処理への応用
        // CPUを使い切るために、プロセッサ数を上限とした並列オプションを作成
        var options = new ParallelOptions
        {
            MaxDegreeOfParallelism = processorCount
        };

        Console.WriteLine("[Process] データ処理を開始します...");

        // ダミーデータ(1〜20の数値)
        var data = Enumerable.Range(1, 20);

        // 指定した並列度で処理を実行
        Parallel.ForEach(data, options, (item) =>
        {
            // 擬似的な重い処理
            SimulateHeavyWork(item);
        });

        Console.WriteLine("[Process] 全ての処理が完了しました。");
    }

    // 重い処理のシミュレーション
    static void SimulateHeavyWork(int id)
    {
        // 現在のスレッドIDを表示して、並列動作していることを確認
        int threadId = Environment.CurrentManagedThreadId;
        Console.WriteLine($" -> Task {id:D2} is running on Thread {threadId}");
        
        // 少し待機
        System.Threading.Thread.Sleep(100);
    }
}

実行結果例(8論理コアのマシンの場合)

[System Info] 論理プロセッサ数: 8
[Process] データ処理を開始します...
 -> Task 01 is running on Thread 4
 -> Task 05 is running on Thread 8
 -> Task 02 is running on Thread 5
 ... (複数のスレッドIDが混在して出力される)
[Process] 全ての処理が完了しました。

カスタムポイント

  • 並列度の調整
    • UIスレッドのフリーズを防ぐためや、他のアプリのためにリソースを残したい場合は、processorCount - 1processorCount / 2 のように値を調整して使用します。
  • コンテナ環境の考慮
    • DockerやKubernetes上でCPU制限(例: 0.5 vCPU)がかかっている場合、.NETのバージョンによってこの値の挙動が異なります。.NET 6以降であれば、コンテナのリソース制限(cgroup)を認識して適切な値を返すよう改善されています。

注意点

  1. 物理コア数ではない
    • 多くのCPUではハイパースレッディング技術により、物理コア数の2倍の値が返されることが一般的です。
  2. 32を超える場合
    • プロセッサ・グループを使用するような非常に大規模なサーバー環境(64コア以上など)では、デフォルト設定ですべてのプロセッサを認識できない場合があります。その場合は <Thread_UseAllCpuGroups> などの構成設定が必要です。
  3. 変動する可能性
    • 通常は一定ですが、仮想マシンや特定の電源管理設定によっては、実行中に利用可能なプロセッサ数が変化したり、アフィニティマスク(使用許可されたCPU)の設定によって制限された値が返ることがあります。

応用

環境に合わせた推奨並列数の算出

CPUバウンド(計算集中の処理)とI/Oバウンド(ファイルや通信待ちの処理)で戦略を変える例です。

public static int GetRecommendedParallelism(bool isCpuBound)
{
    int cores = Environment.ProcessorCount;

    if (isCpuBound)
    {
        // 計算処理ならコア数と同じにする(コンテキストスイッチを減らす)
        return cores;
    }
    else
    {
        // 通信待ちなどならコア数より多くても良い(例: コア数の2倍〜4倍)
        return cores * 2;
    }
}

まとめ

Environment.ProcessorCount は、現在のマシンスペックに応じた柔軟なプログラムを作成するために不可欠なプロパティです。特に大量のデータを扱うバッチ処理や画像処理などで Parallel クラスを使用する際は、この値を基準に並列度を制御することで、ハードウェアリソースを最大限に活かした高速な処理が実現できます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次