@@ -8,15 +8,16 @@ namespace OpenHardwareMonitor.Hardware.Cpu;
88
99internal class CpuLoad
1010{
11- private readonly float [ ] _threadLoads ;
11+ private static readonly bool _queryIdleTimeSeparated = QueryIdleTimeSeparated ( ) ;
1212
13+ private readonly double [ ] _threadLoads ;
14+ private double _totalLoad ;
1315 private long [ ] _idleTimes ;
14- private float _totalLoad ;
1516 private long [ ] _totalTimes ;
1617
1718 public CpuLoad ( CpuId [ ] [ ] cpuid )
1819 {
19- _threadLoads = new float [ cpuid . Sum ( x => x . Length ) ] ;
20+ _threadLoads = new double [ cpuid . Sum ( x => x . Length ) ] ;
2021 _totalLoad = 0 ;
2122
2223 try
@@ -45,30 +46,62 @@ private static bool GetWindowsTimes(out long[] idle, out long[] total)
4546 idle = null ;
4647 total = null ;
4748
48- //Query processor idle information
49+ //use the idle time routine only with a few specific windows 11 versions
50+ if ( _queryIdleTimeSeparated )
51+ return GetWindowsTimesFromIdleTimes ( out idle , out total ) ;
52+
53+ //Query processor performance information
54+ Interop . NtDll . SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION [ ] perfInformation = new Interop . NtDll . SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION [ 64 ] ;
55+ int perfSize = Marshal . SizeOf ( typeof ( Interop . NtDll . SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION ) ) ;
56+ if ( Interop . NtDll . NtQuerySystemInformation ( Interop . NtDll . SYSTEM_INFORMATION_CLASS . SystemProcessorPerformanceInformation , perfInformation , perfInformation . Length * perfSize , out int perfReturn ) != 0 )
57+ return false ;
58+
59+ idle = new long [ perfReturn / perfSize ] ;
60+ total = new long [ perfReturn / perfSize ] ;
61+ for ( int i = 0 ; i < total . Length ; i ++ )
62+ {
63+ idle [ i ] = perfInformation [ i ] . IdleTime ;
64+ total [ i ] = perfInformation [ i ] . KernelTime + perfInformation [ i ] . UserTime ;
65+ }
66+
67+ return true ;
68+ }
69+
70+ private static bool GetWindowsTimesFromIdleTimes ( out long [ ] idle , out long [ ] total )
71+ {
72+ idle = null ;
73+ total = null ;
74+
75+ Interop . NtDll . SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION [ ] perfInformation = new Interop . NtDll . SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION [ 64 ] ;
76+ int perfSize = Marshal . SizeOf ( typeof ( Interop . NtDll . SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION ) ) ;
4977 Interop . NtDll . SYSTEM_PROCESSOR_IDLE_INFORMATION [ ] idleInformation = new Interop . NtDll . SYSTEM_PROCESSOR_IDLE_INFORMATION [ 64 ] ;
5078 int idleSize = Marshal . SizeOf ( typeof ( Interop . NtDll . SYSTEM_PROCESSOR_IDLE_INFORMATION ) ) ;
79+
80+ //Query processor performance and idle information
81+ //these 2 methods must be called as directly as possible one after the other
82+
83+ //Query processor idle information
5184 if ( Interop . NtDll . NtQuerySystemInformation ( Interop . NtDll . SYSTEM_INFORMATION_CLASS . SystemProcessorIdleInformation , idleInformation , idleInformation . Length * idleSize , out int idleReturn ) != 0 )
5285 return false ;
5386
5487 //Query processor performance information
55- Interop . NtDll . SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION [ ] perfInformation = new Interop . NtDll . SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION [ 64 ] ;
56- int perfSize = Marshal . SizeOf ( typeof ( Interop . NtDll . SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION ) ) ;
57- if ( Interop . NtDll . NtQuerySystemInformation ( Interop . NtDll . SYSTEM_INFORMATION_CLASS . SystemProcessorPerformanceInformation ,
58- perfInformation ,
59- perfInformation . Length * perfSize ,
60- out int perfReturn ) != 0 )
61- {
88+ if ( Interop . NtDll . NtQuerySystemInformation ( Interop . NtDll . SYSTEM_INFORMATION_CLASS . SystemProcessorPerformanceInformation , perfInformation , perfInformation . Length * perfSize , out int perfReturn ) != 0 )
6289 return false ;
63- }
6490
65- idle = new long [ idleReturn / idleSize ] ;
66- for ( int i = 0 ; i < idle . Length ; i ++ )
67- idle [ i ] = idleInformation [ i ] . IdleTime ;
91+ int perfItemsCount = perfReturn / perfSize ;
92+ int idleItemsCount = idleReturn / idleSize ;
6893
69- total = new long [ perfReturn / perfSize ] ;
70- for ( int i = 0 ; i < total . Length ; i ++ )
94+ if ( perfItemsCount != idleItemsCount )
95+ return false ;
96+
97+ idle = new long [ perfItemsCount ] ;
98+ total = new long [ perfItemsCount ] ;
99+
100+ for ( int i = 0 ; i < perfItemsCount ; i ++ )
101+ {
102+ idle [ i ] = idleInformation [ i ] . IdleTime ;
71103 total [ i ] = perfInformation [ i ] . KernelTime + perfInformation [ i ] . UserTime ;
104+ }
72105
73106 return true ;
74107 }
@@ -126,12 +159,12 @@ private static bool GetUnixTimes(out long[] idle, out long[] total)
126159 return true ;
127160 }
128161
129- public float GetTotalLoad ( )
162+ public double GetTotalLoad ( )
130163 {
131164 return _totalLoad ;
132165 }
133166
134- public float GetThreadLoad ( int thread )
167+ public double GetThreadLoad ( int thread )
135168 {
136169 return _threadLoads [ thread ] ;
137170 }
@@ -151,28 +184,45 @@ public void Update()
151184 if ( newIdleTimes == null )
152185 return ;
153186
154- float total = 0 ;
187+ double total = 0 ;
155188 int count = 0 ;
156189 for ( int i = 0 ; i < _threadLoads . Length && i < _idleTimes . Length && i < newIdleTimes . Length ; i ++ )
157190 {
158- float idle = ( newIdleTimes [ i ] - _idleTimes [ i ] ) / ( float ) ( newTotalTimes [ i ] - _totalTimes [ i ] ) ;
159- _threadLoads [ i ] = 100f * ( 1.0f - Math . Min ( idle , 1.0f ) ) ;
191+ double idle = ( newIdleTimes [ i ] - _idleTimes [ i ] ) / ( double ) ( newTotalTimes [ i ] - _totalTimes [ i ] ) ;
192+ idle = idle < 0.0 ? 0.0 : idle ;
193+ idle = idle > 1.0 ? 1.0 : idle ;
194+
195+ double load = 100.0 * ( 1.0 - Math . Min ( idle , 1.0 ) ) ;
196+ _threadLoads [ i ] = Math . Round ( load , 2 ) ;
160197 total += idle ;
161198 count ++ ;
162199 }
163200
164201 if ( count > 0 )
165202 {
166- total = 1.0f - ( total / count ) ;
167- total = total < 0 ? 0 : total ;
203+ total = 1.0 - ( total / count ) ;
204+ total = total < 0.0 ? 0.0 : total ;
205+ total = total > 1.0 ? 1.0 : total ;
168206 }
169207 else
170208 {
171209 total = 0 ;
172210 }
173-
174- _totalLoad = total * 100 ;
211+
212+ _totalLoad = Math . Round ( total * 100.0 , 2 ) ;
175213 _totalTimes = newTotalTimes ;
176214 _idleTimes = newIdleTimes ;
177215 }
216+
217+ private static bool QueryIdleTimeSeparated ( )
218+ {
219+ if ( Software . OperatingSystem . IsUnix )
220+ return false ;
221+
222+ // From Windows 11 22H2 the CPU idle time returned by SystemProcessorPerformanceInformation is invalid, this issue has been fixed with 24H2.
223+ OperatingSystem os = Environment . OSVersion ;
224+ Version win1122H2 = new Version ( 10 , 0 , 22621 , 0 ) ;
225+ Version win1124H2 = new Version ( 10 , 0 , 26100 , 0 ) ;
226+ return os . Version >= win1122H2 && os . Version < win1124H2 ;
227+ }
178228}
0 commit comments