mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 03:44:55 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			128 lines
		
	
	
	
		
			4.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			128 lines
		
	
	
	
		
			4.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import _overlapped
 | 
						|
import _thread
 | 
						|
import _winapi
 | 
						|
import math
 | 
						|
import struct
 | 
						|
import winreg
 | 
						|
 | 
						|
 | 
						|
# Seconds per measurement
 | 
						|
SAMPLING_INTERVAL = 1
 | 
						|
# Exponential damping factor to compute exponentially weighted moving average
 | 
						|
# on 1 minute (60 seconds)
 | 
						|
LOAD_FACTOR_1 = 1 / math.exp(SAMPLING_INTERVAL / 60)
 | 
						|
# Initialize the load using the arithmetic mean of the first NVALUE values
 | 
						|
# of the Processor Queue Length
 | 
						|
NVALUE = 5
 | 
						|
 | 
						|
 | 
						|
class WindowsLoadTracker():
 | 
						|
    """
 | 
						|
    This class asynchronously reads the performance counters to calculate
 | 
						|
    the system load on Windows.  A "raw" thread is used here to prevent
 | 
						|
    interference with the test suite's cases for the threading module.
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self):
 | 
						|
        # make __del__ not fail if pre-flight test fails
 | 
						|
        self._running = None
 | 
						|
        self._stopped = None
 | 
						|
 | 
						|
        # Pre-flight test for access to the performance data;
 | 
						|
        # `PermissionError` will be raised if not allowed
 | 
						|
        winreg.QueryInfoKey(winreg.HKEY_PERFORMANCE_DATA)
 | 
						|
 | 
						|
        self._values = []
 | 
						|
        self._load = None
 | 
						|
        self._running = _overlapped.CreateEvent(None, True, False, None)
 | 
						|
        self._stopped = _overlapped.CreateEvent(None, True, False, None)
 | 
						|
 | 
						|
        _thread.start_new_thread(self._update_load, (), {})
 | 
						|
 | 
						|
    def _update_load(self,
 | 
						|
                    # localize module access to prevent shutdown errors
 | 
						|
                     _wait=_winapi.WaitForSingleObject,
 | 
						|
                     _signal=_overlapped.SetEvent):
 | 
						|
        # run until signaled to stop
 | 
						|
        while _wait(self._running, 1000):
 | 
						|
            self._calculate_load()
 | 
						|
        # notify stopped
 | 
						|
        _signal(self._stopped)
 | 
						|
 | 
						|
    def _calculate_load(self,
 | 
						|
                        # localize module access to prevent shutdown errors
 | 
						|
                        _query=winreg.QueryValueEx,
 | 
						|
                        _hkey=winreg.HKEY_PERFORMANCE_DATA,
 | 
						|
                        _unpack=struct.unpack_from):
 | 
						|
        # get the 'System' object
 | 
						|
        data, _ = _query(_hkey, '2')
 | 
						|
        # PERF_DATA_BLOCK {
 | 
						|
        #   WCHAR Signature[4]      8 +
 | 
						|
        #   DWOWD LittleEndian      4 +
 | 
						|
        #   DWORD Version           4 +
 | 
						|
        #   DWORD Revision          4 +
 | 
						|
        #   DWORD TotalByteLength   4 +
 | 
						|
        #   DWORD HeaderLength      = 24 byte offset
 | 
						|
        #   ...
 | 
						|
        # }
 | 
						|
        obj_start, = _unpack('L', data, 24)
 | 
						|
        # PERF_OBJECT_TYPE {
 | 
						|
        #   DWORD TotalByteLength
 | 
						|
        #   DWORD DefinitionLength
 | 
						|
        #   DWORD HeaderLength
 | 
						|
        #   ...
 | 
						|
        # }
 | 
						|
        data_start, defn_start = _unpack('4xLL', data, obj_start)
 | 
						|
        data_base = obj_start + data_start
 | 
						|
        defn_base = obj_start + defn_start
 | 
						|
        # find the 'Processor Queue Length' counter (index=44)
 | 
						|
        while defn_base < data_base:
 | 
						|
            # PERF_COUNTER_DEFINITION {
 | 
						|
            #   DWORD ByteLength
 | 
						|
            #   DWORD CounterNameTitleIndex
 | 
						|
            #   ... [7 DWORDs/28 bytes]
 | 
						|
            #   DWORD CounterOffset
 | 
						|
            # }
 | 
						|
            size, idx, offset = _unpack('LL28xL', data, defn_base)
 | 
						|
            defn_base += size
 | 
						|
            if idx == 44:
 | 
						|
                counter_offset = data_base + offset
 | 
						|
                # the counter is known to be PERF_COUNTER_RAWCOUNT (DWORD)
 | 
						|
                processor_queue_length, = _unpack('L', data, counter_offset)
 | 
						|
                break
 | 
						|
        else:
 | 
						|
            return
 | 
						|
 | 
						|
        # We use an exponentially weighted moving average, imitating the
 | 
						|
        # load calculation on Unix systems.
 | 
						|
        # https://en.wikipedia.org/wiki/Load_(computing)#Unix-style_load_calculation
 | 
						|
        # https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
 | 
						|
        if self._load is not None:
 | 
						|
            self._load = (self._load * LOAD_FACTOR_1
 | 
						|
                            + processor_queue_length  * (1.0 - LOAD_FACTOR_1))
 | 
						|
        elif len(self._values) < NVALUE:
 | 
						|
            self._values.append(processor_queue_length)
 | 
						|
        else:
 | 
						|
            self._load = sum(self._values) / len(self._values)
 | 
						|
 | 
						|
    def close(self, kill=True):
 | 
						|
        self.__del__()
 | 
						|
        return
 | 
						|
 | 
						|
    def __del__(self,
 | 
						|
                # localize module access to prevent shutdown errors
 | 
						|
                _wait=_winapi.WaitForSingleObject,
 | 
						|
                _close=_winapi.CloseHandle,
 | 
						|
                _signal=_overlapped.SetEvent):
 | 
						|
        if self._running is not None:
 | 
						|
            # tell the update thread to quit
 | 
						|
            _signal(self._running)
 | 
						|
            # wait for the update thread to signal done
 | 
						|
            _wait(self._stopped, -1)
 | 
						|
            # cleanup events
 | 
						|
            _close(self._running)
 | 
						|
            _close(self._stopped)
 | 
						|
            self._running = self._stopped = None
 | 
						|
 | 
						|
    def getloadavg(self):
 | 
						|
        return self._load
 |