- 
                Notifications
    
You must be signed in to change notification settings  - Fork 5.2k
 
Description
When the process is affinitized on startup, the behavior of Environment.ProcessorCount currently differs between Windows and Linux. On Linux, the property returns the number of processors available according to the affinity mask. On Windows, the property returns the number of processors configured in the system and ignores the affinity mask. The property is often used to determine the amount of parallelism to use in the process, and it has been observed that not limiting the property's value based on affinitization can lead to worse performance.
On Linux, CPU resource limits placed through cgroups also affect the value returned by the property, as we have found through trial-and-error that returning the restricted number of processors from this property is the most appropriate behavior that makes most callers work well.
Goal
- Have 
Environment.ProcessorCountreturn a value that represents an appropriate level of parallelism that may be used in the process based on affinitization and CPU resource limits - Have the property return a consistent value in comparable relevant configuration between Windows and Linux
 
Behavior before change
On a machine with 8 logical processors:
- When the process is affinitized to 1 logical processor:
- Windows: 
Environment.ProcessorCount == 8 - Linux: 
Environment.ProcessorCount == 1 
 - Windows: 
 - When CPU resources for the process are limited to the equivalent of 1 logical processor:
- Windows: 
Environment.ProcessorCount == 8 - Linux: 
Environment.ProcessorCount == 1 
 - Windows: 
 
Behavior after #45943
- When the process is affinitized to 1 logical processor:
- Windows: 
Environment.ProcessorCount == 1 - Linux: 
Environment.ProcessorCount == 1 
 - Windows: 
 - When CPU resources for the process are limited to the equivalent of 1 logical processor:
- Windows: 
Environment.ProcessorCount == 8 - Linux: 
Environment.ProcessorCount == 1 
 - Windows: 
 
Behavior after other such issues are fixed (no PR yet)
- When the process is affinitized to 1 logical processor:
- Windows: 
Environment.ProcessorCount == 1 - Linux: 
Environment.ProcessorCount == 1 
 - Windows: 
 - When CPU resources for the process are limited to the equivalent of 1 logical processor:
- Windows: 
Environment.ProcessorCount == 1 - Linux: 
Environment.ProcessorCount == 1 
 - Windows: 
 
Versions affected
.NET 6
Potential breaks on Windows and workarounds
- Code that uses 
Environment.ProcessorCountand scales down parallelism based on other app or system configuration (including potentially the process' affinity mask) may end up using lower parallelism than intended- Workaround may be to update the application's parallelism scaling
 
 - Code that expects 
Environment.ProcessorCountto return the configured logical processor count may have unexpected results. For example code that verifies that the process is not started affinitized by comparing the affinitized processor count toEnvironment.ProcessorCountwould fail to detect affinitized startup because the comparison would be equal. Or the process may intend to display the total processor count.- Workaround may be to pinvoke to get the total processor count. May not be ideal. Providing full information in other APIs would need discussion to determine what to provide, for example, number of processors configured in the system, number of processors that are online/offline, number of processors the process is affinitized to, CPU quota limits, etc.
 
 - Code that happens to perform worse when the value returned by 
Environment.ProcessorCountis limited by affinitization or CPU resource limits- If these become common, it may be necessary to provide a workaround, such as to provide a way to configure what 
Environment.ProcessorCountwould return, or to provide other APIs 
 - If these become common, it may be necessary to provide a workaround, such as to provide a way to configure what 
 
Currently, a config option is not provided to revert the behavior. The difference in behavior between Windows and Linux existed in .NET 5 and below, where Linux has not offered a config option to provide the total processor count, so there hasn't been a workaround for the above issues on Linux so far.
Non-breaking
- The thread pool already took affinitization into account in the default parallelization it uses on Windows, when the process is not configured to use multiple CPU groups (the default). In .NET 6 thread pool worker thread management was migrated to managed code and uses 
Environment.ProcessorCount, so after UpdateEnvironment.ProcessorCounton Windows to take into account the processor affinity mask #45943 thread pool worker thread parallelization in .NET 6 would be the same as before in that scenario.