Optimizing Parallel Processing by Getting the Number of Logical Processors in C#

目次

Overview

This article explains how to get the number of logical processors (logical cores) on the machine where your application is running. This information is used to optimize the degree of parallelism (number of threads) based on hardware resources when performing parallel processing with Parallel.ForEach or PLINQ.

Specifications (Input/Output)

  • Input: None (refers to the hardware information of the execution environment).
  • Output: The number of logical processors available to the current process (int).
  • Definition: If technologies like Hyper-Threading are enabled, this returns the number of logical threads recognized by the OS, not the number of physical cores.

Basic Usage

You can retrieve this as an integer simply by referencing the Environment.ProcessorCount property.

// Returns "8" if the CPU has 4 cores and 8 threads
int logicalCores = Environment.ProcessorCount;
Console.WriteLine($"Number of logical cores: {logicalCores}");

Full Code

The following is an example of a console application that dynamically sets the maximum degree of parallelism for a task using the retrieved processor count.

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

class Program
{
    static void Main()
    {
        // 1. Get the number of logical processors
        int processorCount = Environment.ProcessorCount;
        
        Console.WriteLine($"[System Info] Logical Processors: {processorCount}");

        // 2. Application to parallel processing
        // Create parallel options with the processor count as the upper limit to utilize the CPU fully
        var options = new ParallelOptions
        {
            MaxDegreeOfParallelism = processorCount
        };

        Console.WriteLine("[Process] Starting data processing...");

        // Dummy data (values 1 to 20)
        var data = Enumerable.Range(1, 20);

        // Execute processing with the specified degree of parallelism
        Parallel.ForEach(data, options, (item) =>
        {
            // Simulate a heavy task
            SimulateHeavyWork(item);
        });

        Console.WriteLine("[Process] All processes completed.");
    }

    // Simulation of a heavy process
    static void SimulateHeavyWork(int id)
    {
        // Display current thread ID to confirm parallel execution
        int threadId = Environment.CurrentManagedThreadId;
        Console.WriteLine($" -> Task {id:D2} is running on Thread {threadId}");
        
        // Wait for a short time
        System.Threading.Thread.Sleep(100);
    }
}

Execution Result Example (on a machine with 8 logical cores)

[System Info] Logical Processors: 8
[Process] Starting data processing...
 -> Task 01 is running on Thread 4
 -> Task 05 is running on Thread 8
 -> Task 02 is running on Thread 5
 ... (Output mixed with multiple thread IDs)
[Process] All processes completed.

Customization Points

  • Adjusting Parallelism: If you want to prevent the UI thread from freezing or leave resources for other applications, adjust the value, such as processorCount - 1 or processorCount / 2.
  • Container Environments: If CPU limits (e.g., 0.5 vCPU) are applied in Docker or Kubernetes, the behavior of this value depends on the .NET version. In .NET 6 and later, it has been improved to recognize container resource limits (cgroups) and return an appropriate value.

Points of Caution

  • Not Physical Cores: For many CPUs, this property typically returns twice the number of physical cores due to Hyper-Threading technology.
  • Cases Exceeding 32: In large-scale server environments (64 cores or more) that use processor groups, the default settings might not recognize all processors. In such cases, configuration settings like <Thread_UseAllCpuGroups> are required.
  • Potential for Variation: While usually constant, the number of available processors can change in virtual machines or specific power management settings. It may also return a limited value based on affinity mask settings (CPU usage permissions).

Application

Calculating Recommended Parallelism Based on Environment

This example shows how to change strategy between CPU-bound tasks (calculation-heavy) and I/O-bound tasks (waiting for files or communication).

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

    if (isCpuBound)
    {
        // For calculation tasks, match the core count to reduce context switching
        return cores;
    }
    else
    {
        // For communication or I/O, it can be higher than the core count (e.g., 2x to 4x)
        return cores * 2;
    }
}

Summary

Environment.ProcessorCount is an essential property for creating flexible programs that match the current machine specifications. Especially when using the Parallel class for batch or image processing involving large amounts of data, controlling the degree of parallelism based on this value enables high-speed processing that maximizes hardware resources.

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

この記事を書いた人

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

目次