summaryrefslogtreecommitdiffstats
path: root/Lib/test/libregrtest/win_utils.py
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2019-10-03 14:15:16 (GMT)
committerGitHub <noreply@github.com>2019-10-03 14:15:16 (GMT)
commit098e25672f1c3578855d5ded4f5147795c9ed956 (patch)
tree80a8a09e94f5ec3c1a5dcc0538db23ff63754674 /Lib/test/libregrtest/win_utils.py
parentc65119d5bfded03f80a9805889391b66fa7bf551 (diff)
downloadcpython-098e25672f1c3578855d5ded4f5147795c9ed956.zip
cpython-098e25672f1c3578855d5ded4f5147795c9ed956.tar.gz
cpython-098e25672f1c3578855d5ded4f5147795c9ed956.tar.bz2
bpo-36670: Enhance regrtest (GH-16556)
* Add log() method: add timestamp and load average prefixes to main messages. * WindowsLoadTracker: * LOAD_FACTOR_1 is now computed using SAMPLING_INTERVAL * Initialize the load to the arithmetic mean of the first 5 values of the Processor Queue Length value (so over 5 seconds), rather than 0.0. * Handle BrokenPipeError and when typeperf exit. * format_duration(1.5) now returns '1.5 sec', rather than '1 sec 500 ms'
Diffstat (limited to 'Lib/test/libregrtest/win_utils.py')
-rw-r--r--Lib/test/libregrtest/win_utils.py66
1 files changed, 47 insertions, 19 deletions
diff --git a/Lib/test/libregrtest/win_utils.py b/Lib/test/libregrtest/win_utils.py
index 0b73a94..028c011 100644
--- a/Lib/test/libregrtest/win_utils.py
+++ b/Lib/test/libregrtest/win_utils.py
@@ -1,4 +1,5 @@
import _winapi
+import math
import msvcrt
import os
import subprocess
@@ -10,11 +11,14 @@ from test.libregrtest.utils import print_warning
# Max size of asynchronous reads
BUFSIZE = 8192
-# Exponential damping factor (see below)
-LOAD_FACTOR_1 = 0.9200444146293232478931553241
-
# 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
# Windows registry subkey of HKEY_LOCAL_MACHINE where the counter names
# of typeperf are registered
COUNTER_REGISTRY_KEY = (r"SOFTWARE\Microsoft\Windows NT\CurrentVersion"
@@ -30,10 +34,10 @@ class WindowsLoadTracker():
"""
def __init__(self):
- self.load = 0.0
- self.counter_name = ''
+ self._values = []
+ self._load = None
self._buffer = ''
- self.popen = None
+ self._popen = None
self.start()
def start(self):
@@ -65,7 +69,7 @@ class WindowsLoadTracker():
# Spawn off the load monitor
counter_name = self._get_counter_name()
command = ['typeperf', counter_name, '-si', str(SAMPLING_INTERVAL)]
- self.popen = subprocess.Popen(' '.join(command), stdout=command_stdout, cwd=support.SAVEDCWD)
+ self._popen = subprocess.Popen(' '.join(command), stdout=command_stdout, cwd=support.SAVEDCWD)
# Close our copy of the write end of the pipe
os.close(command_stdout)
@@ -85,12 +89,16 @@ class WindowsLoadTracker():
process_queue_length = counters_dict['44']
return f'"\\{system}\\{process_queue_length}"'
- def close(self):
- if self.popen is None:
+ def close(self, kill=True):
+ if self._popen is None:
return
- self.popen.kill()
- self.popen.wait()
- self.popen = None
+
+ self._load = None
+
+ if kill:
+ self._popen.kill()
+ self._popen.wait()
+ self._popen = None
def __del__(self):
self.close()
@@ -109,7 +117,7 @@ class WindowsLoadTracker():
value = value[1:-1]
return float(value)
- def read_lines(self):
+ def _read_lines(self):
overlapped, _ = _winapi.ReadFile(self.pipe, BUFSIZE, True)
bytes_read, res = overlapped.GetOverlappedResult(False)
if res != 0:
@@ -135,7 +143,21 @@ class WindowsLoadTracker():
return lines
def getloadavg(self):
- for line in self.read_lines():
+ if self._popen is None:
+ return None
+
+ returncode = self._popen.poll()
+ if returncode is not None:
+ self.close(kill=False)
+ return None
+
+ try:
+ lines = self._read_lines()
+ except BrokenPipeError:
+ self.close()
+ return None
+
+ for line in lines:
line = line.rstrip()
# Ignore the initial header:
@@ -148,7 +170,7 @@ class WindowsLoadTracker():
continue
try:
- load = self._parse_line(line)
+ processor_queue_length = self._parse_line(line)
except ValueError:
print_warning("Failed to parse typeperf output: %a" % line)
continue
@@ -156,7 +178,13 @@ class WindowsLoadTracker():
# 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
- new_load = self.load * LOAD_FACTOR_1 + load * (1.0 - LOAD_FACTOR_1)
- self.load = new_load
-
- return self.load
+ # 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)
+
+ return self._load