r1064 - trunk
by gnichols@fedoraproject.org
Author: gnichols
Date: 2011-09-14 13:55:50 +0000 (Wed, 14 Sep 2011)
New Revision: 1064
Modified:
trunk/Makefile
Log:
R21
Modified: trunk/Makefile
===================================================================
--- trunk/Makefile 2011-09-14 13:53:42 UTC (rev 1063)
+++ trunk/Makefile 2011-09-14 13:55:50 UTC (rev 1064)
@@ -14,7 +14,7 @@
# Author: Greg Nichols
V7_VERSION := 1.4
-V7_RELEASE := 20
+V7_RELEASE := 21
V7_VERSION_RELEASE := $(V7_VERSION)-$(V7_RELEASE)
V7_VERSION_PY := v7/version.py
V7_SVN_MODULE := svn+ssh://svn.fedorahosted.org/svn/v7
12 years, 8 months
r1063 - trunk/tests/cpuscaling
by gnichols@fedoraproject.org
Author: gnichols
Date: 2011-09-14 13:53:42 +0000 (Wed, 14 Sep 2011)
New Revision: 1063
Added:
trunk/tests/cpuscaling/aperfmperf.py
Modified:
trunk/tests/cpuscaling/Makefile
trunk/tests/cpuscaling/cpuscaling.py
Log:
Bug 732113 - cpuscaling rewrite
Modified: trunk/tests/cpuscaling/Makefile
===================================================================
--- trunk/tests/cpuscaling/Makefile 2011-08-31 20:43:59 UTC (rev 1062)
+++ trunk/tests/cpuscaling/Makefile 2011-09-14 13:53:42 UTC (rev 1063)
@@ -15,7 +15,7 @@
.PHONY: all install download clean
-FILES=runtest.sh cpuscaling.py
+FILES=runtest.sh cpuscaling.py aperfmperf.py
run: $(FILES) build
./runtest.sh
Added: trunk/tests/cpuscaling/aperfmperf.py
===================================================================
--- trunk/tests/cpuscaling/aperfmperf.py (rev 0)
+++ trunk/tests/cpuscaling/aperfmperf.py 2011-09-14 13:53:42 UTC (rev 1063)
@@ -0,0 +1,200 @@
+#!/usr/bin/python
+# Copyright (c) 2008 Red Hat, Inc. All rights reserved. This copyrighted material
+# is made available to anyone wishing to use, modify, copy, or
+# redistribute it subject to the terms and conditions of the GNU General
+# Public License v.2.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+import struct
+
+
+
+class EffectiveFrequency:
+ """
+ Simple mechanism to use the Effective Frequency Interface
+ """
+
+ def __init__(self, cpunum):
+ """
+ @param cpunum: Number of the CPU as used by the OS
+ @type cpunum: int
+ """
+ self.__cpunum = int(cpunum)
+ self.__aperf = -1
+ self.__mperf = -1
+
+ def has_support(self):
+ """
+ Check for Effective Frequency Interface support
+
+ This information is stored in CPUID function 0000_0006,
+ register ECX, Bit 0.
+
+ @return: Effective Frequency Interface support (0/1)
+ @rtype : int
+ """
+ support = 0
+ cpuidregs = self.__read_cpuid(0x00000006)
+ if cpuidregs:
+ support = cpuidregs[2] & 0x1
+ return support
+
+ @staticmethod
+ def __read_file(path, offset, size):
+ """
+ Read a specified amount of bytes from a file starting at some offset
+
+ @param path : The full path to the file
+ @type path : str
+ @param offset: The byte offset at which the read should start
+ @type offset: int
+ @param size : The amount of bytes to be read
+ @param size : int
+ @return : The chunk of the file
+ @rtype : str
+ """
+ filedesc = None
+ try:
+ filedesc = open(path, "rb")
+ filedesc.seek(offset, 1)
+ value = filedesc.read(size)
+ except IOError:
+ value = ""
+ if filedesc:
+ filedesc.close()
+ if len(value) != size:
+ value = ""
+ return value
+
+ def __read_msr(self, msr):
+ """
+ Read the content of a MSR
+
+ Returns the 64-bit value of the given MSR, or -1 in case of errors.
+
+ @param msr: MSR number
+ @type msr: int
+ @return : 64-bit value of the MSR
+ @rtype : int
+ """
+ path = "/dev/cpu/%d/msr" % (self.__cpunum, )
+ value = self.__read_file(path, msr, 8)
+ if len(value) == 8:
+ value = struct.unpack("Q", value)[0]
+ else:
+ value = -1
+ return value
+
+ def __read_cpuid(self, function, index=0):
+ """
+ Read the register values for a CPUID function
+
+ Returns a tuple with values for the registers EAX, EBX, ECX
+ and EDX, or an empty tuple in case of errors.
+
+ @param function: CPUID instruction function
+ @type function: int
+ @param index : CPUID instruction index (value of ECX)
+ @type index : int
+ @return : Values for registers EAX, EBX, ECX, and EDX
+ @rtype : tuple
+ """
+ path = "/dev/cpu/%d/cpuid" % (self.__cpunum, )
+ function &= 0xffffffff
+ index &= 0xffffffff
+ offset = function | (index << 32)
+ value = self.__read_file(path, offset, 16)
+ if len(value) == 16:
+ value = struct.unpack("4I", value)
+ else:
+ value = tuple()
+ return value
+
+ def __read_aperf_mperf(self):
+ """
+ Read APERF and MPERF MSRs
+
+ Returns a tuple with the values of the APERF and MPERF MSRs.
+ Both values will be set to -1 in case of errors or if EFRI
+ is not supported by the CPU.
+
+ @return: Values of APERF and MPERF MSRs
+ @rtype : tuple
+ """
+ aperf, mperf = (-1, -1)
+ if self.has_support():
+ aperf = self.__read_msr(0x000000E8)
+ mperf = self.__read_msr(0x000000E7)
+ if -1 in (aperf, mperf):
+ aperf, mperf = (-1, -1)
+ return aperf, mperf
+
+ def __get_max_frequency(self):
+ """
+ Get the highest non-boosted frequency
+
+ The highest non-boosted frequency is the frequency of the
+ P-State 0, using the software P-State numbering.
+
+ @return: Highest non-boosted frequency in kHz, or -1 on error
+ @rtype : int
+ """
+ sysfs = "/sys/devices/system/cpu/cpu%d/" \
+ "cpufreq/scaling_available_frequencies" % (self.__cpunum, )
+ try:
+ filedesc = open(sysfs, "r")
+ freqs = filedesc.readline()
+ filedesc.close()
+ frequency = max([int(freq) for freq in freqs.split()])
+ except (IOError, ValueError):
+ frequency = -1
+ return frequency
+
+ def start_sampling(self):
+ """
+ Start the frequency sampling period
+
+ Read APERF and MPERF MSRs and store the results as initial
+ values for use as offsets in later calculations.
+
+ @return: Success of the reading
+ @rtype : bool
+ """
+ self.__aperf, self.__mperf = self.__read_aperf_mperf()
+ if -1 in (self.__aperf, self.__mperf):
+ return False
+ return True
+
+ def stop_sampling(self):
+ """
+ Stop the sampling period and return the effective frequency
+
+ Reads the APERF and MPERF MSRs once again, does the required
+ calculations, and returns the effective frequency during the
+ sampling period.
+
+ @return: Effective frequency in kHz, or -1 on error
+ @rtype : int
+ """
+ frequency = -1
+ aperf, mperf = self.__read_aperf_mperf()
+ maxfreq = self.__get_max_frequency()
+ if -1 not in (self.__aperf, self.__mperf, aperf, mperf, maxfreq):
+ aperfdiff = aperf - self.__aperf
+ mperfdiff = mperf - self.__mperf
+ if struct.unpack("L", struct.pack("l", -1))[0] / 100 < aperfdiff:
+ shiftcnt = 7
+ aperfdiff >>= shiftcnt
+ mperfdiff >>= shiftcnt
+ perfpercent = (aperfdiff * 100) / mperfdiff
+ frequency = maxfreq * perfpercent / 100
+ self.__aperf, self.__mperf = (-1, -1)
+ return frequency
\ No newline at end of file
Modified: trunk/tests/cpuscaling/cpuscaling.py
===================================================================
--- trunk/tests/cpuscaling/cpuscaling.py 2011-08-31 20:43:59 UTC (rev 1062)
+++ trunk/tests/cpuscaling/cpuscaling.py 2011-09-14 13:53:42 UTC (rev 1063)
@@ -29,6 +29,8 @@
from v7.device import Device
from v7.command import Command, V7CommandException
+from aperfmperf import EffectiveFrequency
+
"""
Tolerance Calculation, workload time checks:
@@ -67,9 +69,34 @@
self.workloadNames = {"minimum": "User Space (min)", "maximum": "User Space (max)", "ondemand": "On Demand", "performance": "Performance"}
self.workloadTime = dict() # workload run time list indexed by [workload][cpu]
self.workloadFreq = dict() # measured cpu freq indexed by [workload][cpu]
+ self.workloadEffectiveFreq = dict() # measured effective cpu freq indexed by [workload][cpu]
self.workloadErrors = dict() # error log indexed by [workload]
self.currentWorkload = "minimum"
+ def dumpTables(self):
+ print "\nDumping Data Tables:"
+ for workload in self.workloads:
+ print "workload: " + workload
+ print "-----------------------------------------------------------------------------------"
+ if workload in self.workloadTime:
+ for (cpu, times) in self.workloadTime[workload].iteritems():
+ sys.stdout.write("cpu%s: " % cpu)
+ for time in times:
+ sys.stdout.write(" %.2fs" % time)
+ print ""
+ if workload in self.workloadFreq:
+ for (cpu, freqs) in self.workloadFreq[workload].iteritems():
+ sys.stdout.write("cpu%s: " % cpu)
+ for freq in freqs:
+ sys.stdout.write(" %4u MHz" % freq)
+ print ""
+ if workload in self.workloadEffectiveFreq:
+ for (cpu, freqs) in self.workloadEffectiveFreq[workload].iteritems():
+ sys.stdout.write("cpu%s: " % cpu)
+ for freq in freqs:
+ sys.stdout.write(" %4u MHz" % freq)
+ print ""
+
def setWorkloadTime(self, workload, cpu, time):
if not workload in self.workloadTime:
self.workloadTime[workload] = dict()
@@ -85,6 +112,14 @@
mhz = int(freq)/1000
self.workloadFreq[workload][cpu].append(mhz)
+ def setWorkloadEffectiveFreq(self, workload, cpu, freq):
+ if not workload in self.workloadEffectiveFreq:
+ self.workloadEffectiveFreq[workload] = dict()
+ if not cpu in self.workloadEffectiveFreq[workload]:
+ self.workloadEffectiveFreq[workload][cpu] = list()
+ mhz = int(freq)/1000
+ self.workloadEffectiveFreq[workload][cpu].append(mhz)
+
def logWorkloadError(self, errorMessage):
print errorMessage
if not self.currentWorkload in self.workloadErrors:
@@ -97,9 +132,9 @@
except KeyError:
return None
- def getWorkloadFreq(self, workload, cpu):
+ def getWorkloadFreq(self, table, workload, cpu):
try:
- return self.workloadFreq[workload][cpu][-1] # get the last time
+ return table[workload][cpu][-1] # get the last freq
except KeyError:
return None
@@ -283,14 +318,15 @@
# otherwise, set command has failed
return None
- def getFrequency(self, cpu=None):
+ def getFrequency(self, cpu=None, ignoreErrors=False):
if not cpu:
- cpuInfoCurrentFreq = self.getPackageParameter("cpuinfo_cur_freq", logging=True)
- # scalingCurrentFreq = self.getPackageParameter("scaling_cur_freq", logging=True)
+ print "\nChecking current cpu frequency:"
+ cpuInfoCurrentFreq = self.getPackageParameter("cpuinfo_cur_freq", logging=True, ignoreErrors=ignoreErrors)
+ scalingCurrentFreq = self.getPackageParameter("scaling_cur_freq", logging=True, ignoreErrors=True)
# if cpuInfoCurrentFreq != scalingCurrentFreq:
# print "Warning: scaling_cur_freq is %s while cpuinfo_cur_freq is %s" % ( scalingCurrentFreq, cpuInfoCurrentFreq)
if cpuInfoCurrentFreq:
- # print "Using cpuinfo_cur_freq"
+ print "Using cpuinfo_cur_freq"
return cpuInfoCurrentFreq
# otherwise
@@ -301,9 +337,11 @@
self.logWorkloadError("Error: could not determine frequency")
return None
else:
- cpuInfoCurrentFreq = self.getParameter(cpu, "cpuinfo_cur_freq")
- # scalingCurrentFreq = self.getParameter(cpu, "scaling_cur_freq")
+ print "\nChecking current frequency for cpu%s" % cpu
+ cpuInfoCurrentFreq = self.getParameter(cpu, "cpuinfo_cur_freq", logging=True)
+ scalingCurrentFreq = self.getParameter(cpu, "scaling_cur_freq", logging=True)
if cpuInfoCurrentFreq:
+ print "Using cpuinfo_cur_freq"
return cpuInfoCurrentFreq
# otherwise
@@ -317,20 +355,24 @@
def setGovernor(self, governor):
return self.setPackageParameter("g", "scaling_governor", "scaling_governor", governor)
- def getPackageParameter(self, parameter, logging=False):
+ def getPackageParameter(self, parameter, logging=False, ignoreErrors=False):
packageParameterValue = None
+ success = True
for cpu in self.packageToCpus[self.currentPackage]:
value = self.getParameter(cpu, parameter)
if logging:
print "cpu%s %s = %s" % (cpu, parameter, value)
if value and not packageParameterValue:
packageParameterValue = value
- elif value != packageParameterValue:
+ elif value != packageParameterValue and not ignoreErrors:
self.logWorkloadError("Error: cpu%s in package %s has the value %s which differs from other cpus in the package" % (cpu, self.currentPackage, value))
- return None
- return packageParameterValue
+ success = False
+ if success:
+ return packageParameterValue
+
+ return None
- def getParameter(self, cpu, parameter):
+ def getParameter(self, cpu, parameter, logging=False):
value = None
parameterFilePath = "%s/%s" % (self.cpufreqDirectories[cpu], parameter)
try:
@@ -340,6 +382,8 @@
self.logWorkloadError("Error: failed to get %s for %s" % (parameter, self.cpufreqDirectories[cpu]))
return None
value = line.strip()
+ if logging:
+ print "cpu%s %s = %s" % (cpu, parameter, value)
return value
except IOError, exception:
self.logWorkloadError("Error: could not open %s" % parameterFilePath)
@@ -385,15 +429,15 @@
self.logWorkloadError("Error: could not repeat load test times within %.1f%%" % self.retryTolerance)
return None
- def __runSubprocessLoadTest(self, cpu=None):
+ def __runSubprocessLoadTest(self, singleCpu=None):
print "Running load test for package " + self.currentPackage
self.loadProcesses = dict()
# if cpu is set, just load that one
- if cpu:
- loadProcess = LoadProcess(self, cpu)
+ if singleCpu:
+ loadProcess = LoadProcess(self, singleCpu)
loadProcess.start()
- self.loadProcesses[cpu] = loadProcess
+ self.loadProcesses[singleCpu] = loadProcess
else: # load all cpus in the package
for cpu in self.packageToCpus[self.currentPackage]:
loadProcess = LoadProcess(self, cpu)
@@ -407,10 +451,11 @@
for (cpu, process) in self.loadProcesses.items():
if process.isDone():
if process.returnCode is not 0:
- self.logWorkloadError("Error: load process terminated with return code " + process.returnCode)
+ self.logWorkloadError("Error: load process terminated with return code %s" % process.returnCode)
return None
runTime = process.getRunTime()
frequency = self.getFrequency(cpu)
+ effectiveFrequency = process.getEffectiveFrequency()
if not runTime:
self.logWorkloadError("Error: could not find process run time")
return None
@@ -419,12 +464,19 @@
return None
# otherwise - collect info
print "process for cpu %s is done in %.2f seconds, at %u MHz" % (cpu, runTime, int(int(frequency)/1000))
+ if effectiveFrequency:
+ print "process effective frequency: %u MHz" % (effectiveFrequency/1000)
self.setWorkloadTime(self.currentWorkload, cpu, runTime)
self.setWorkloadFreq(self.currentWorkload, cpu, frequency)
+ self.setWorkloadEffectiveFreq(self.currentWorkload, cpu, effectiveFrequency)
totalProcessTime += runTime
del self.loadProcesses[cpu]
time.sleep(1) # poll once per second
+ # if doing a single cpu, log its current freq while waiting
+ if singleCpu:
+ runningFreq = self.getFrequency(cpu)
+
print "processes complete"
return totalProcessTime/numberOfProcesses
@@ -584,7 +636,7 @@
# 7. Set the the cpu speed to it's lowest value
frequency = self.frequencies[0]
- currentFrequency = self.getFrequency()
+ currentFrequency = self.getFrequency(ignoreErrors=True)
if currentFrequency:
print "Changing cpu frequency from %u to %u MHz" % (int(currentFrequency)/1000, (frequency/1000))
else:
@@ -611,7 +663,7 @@
# 11. Set the cpu speed to it's highest value as above.
frequency = self.frequencies[-1]
- currentFrequency = self.getFrequency()
+ currentFrequency = self.getFrequency(ignoreErrors=True)
print "Changing cpu frequency from %u to %u MHz" % (int(currentFrequency)/1000, (frequency/1000))
if not self.setFrequency("%u" % frequency):
success = False
@@ -723,7 +775,7 @@
self.logWorkloadError("Error: could not determine current frequency")
return False
elif not self.checkFrequency(maximumFrequency, currentFrequency):
- self.logWorkloadError("Error: Current cpu frequency of %.2f is not set to the maximum value of %.2f" % (currentFrequency, maximumFrequency))
+ self.logWorkloadError("Error: Current cpu frequency of %s is not set to the maximum value of %s" % (currentFrequency, maximumFrequency))
return False
# 22. Repeat workload test, record timing, also verify frequency does not drop during run
@@ -785,8 +837,9 @@
success = False
else:
- self.logWorkloadError("Error: %s governor not supported" % governor)
- success = False
+ # some older systems don't support ondemand at all.
+ self.logWorkloadError("Warning: %s governor not supported" % governor)
+ success = True
return success
@@ -823,9 +876,36 @@
def printSummary(self, package):
+ if self.debug != Constants.off:
+ self.dumpTables()
+
+ self.printFreqSummary("CPU Frequency Test", package, self.workloadFreq)
+ self.printFreqSummary("CPU Effective Frequency", package, self.workloadEffectiveFreq)
+
+
+ if "ondemand" in self.workloadTime:
+ print "\nCPU Workload Test:\n"
+ print "Expected Speedup: %.2f" % self.predictedSpeedup
+ print "Allowable Speedup: %.2f to %.2f" % (self.minimumRequiredSpeedup(), self.maximumRequiredSpeedup())
+ print ""
+ print " On Demand "
+ print "-------- -------------- "
+
+ #actual values, one row per cpu
+ workload = "ondemand"
+ for cpu in self.packageToCpus[package]:
+ workloadTime = self.getWorkloadTime(workload, cpu)
+ if workloadTime:
+ sys.stdout.write("cpu %-3s " % cpu)
+ sys.stdout.write(" %.2f" % self.speedup(cpu, workload))
+ sys.stdout.write((" (%4uMHz, %.2fs) " % (self.getWorkloadFreq(self.workloadEffectiveFreq, workload, cpu) ,workloadTime)[:10]))
+ print ""
+
+ print ""
+ def printFreqSummary(self, title, package, freqTable):
print ""
print "Summary for Package %s:" % package
- print "\nCPU Frequency Test:\n"
+ print "\n%s:\n" % title
print " User Min User Max Performance"
print "-------- -------------- -------------- --------------"
@@ -839,31 +919,12 @@
sys.stdout.write("cpu %-3s " % cpu)
for workload in self.workloads:
if workload in self.workloadTime and workload != "ondemand" and workload != "turbo":
- sys.stdout.write(" %4u" % self.getWorkloadFreq(workload, cpu))
+ sys.stdout.write(" %4u" % self.getWorkloadFreq(freqTable, workload, cpu))
sys.stdout.write((" (%.2fs) " % self.getWorkloadTime(workload, cpu))[:10])
print ""
print ""
- if "ondemand" in self.workloadTime:
- print "\nCPU Workload Test:\n"
- print "Expected Speedup: %.2f" % self.predictedSpeedup
- print "Allowable Speedup: %.2f to %.2f" % (self.minimumRequiredSpeedup(), self.maximumRequiredSpeedup())
- print ""
- print " On Demand "
- print "-------- -------------- "
-
- #actual values, one row per cpu
- for cpu in self.packageToCpus[package]:
- workloadTime = self.getWorkloadTime("ondemand", cpu)
- if workloadTime:
- sys.stdout.write("cpu %-3s " % cpu)
- sys.stdout.write(" %.2f" % self.speedup(cpu, "ondemand"))
- sys.stdout.write((" (%.2fs) " % workloadTime)[:10])
- print ""
-
- print ""
-
def printWorkloadTimeSummary(self, package):
sys.stdout.write("Workload run times for package %s:\n" % package)
sys.stdout.write("-------------------\n")
@@ -939,6 +1000,7 @@
self.returnCode = None
self.start_elapsed_time = None
self.runTime = None
+ self.effectiveFrequency = None
# signal.signal(signal.SIGCHLD, self.sigChldHandler)
def start(self):
@@ -961,6 +1023,9 @@
def getRunTime(self):
return self.runTime
+ def getEffectiveFrequency(self):
+ return self.effectiveFrequency
+
def sigChldHandler(self, signalNumber, stackFrame):
print "Got signal %d" % signalNumber
self.__stopTimer()
@@ -974,12 +1039,13 @@
line = self.workProcess.readline()
if line:
try:
- self.runTime = float(line)
- return True
- except ValueError, e:
- pass # keep trying
+ if line.startswith("runTime: "):
+ self.runTime = float(line.split()[1])
+ elif line.startswith("eftFreq: "):
+ self.effectiveFrequency = int(line.split()[1])
+ except (ValueError, IndexError):
+ pass # keep trying
else:
- print "Error: could not determine process run time"
break
return True
# otherwise
@@ -1005,12 +1071,22 @@
if __name__ == "__main__":
if "work" in sys.argv:
+ effectiveFrequencyInterface = None
+ effectiveFrequency = 0
+ if len(sys.argv) > 2 and sys.argv[2].isdigit():
+ cpuNumber = int(sys.argv[2])
+ effectiveFrequencyInterface = EffectiveFrequency(cpuNumber)
+ effectiveFrequencyInterface.start_sampling()
(start_utime, start_stime, start_cutime, start_cstime, start_elapsed_time) = os.times()
pi()
(stop_utime, stop_stime, stop_cutime, stop_cstime, stop_elapsed_time) = os.times()
- runTime = stop_elapsed_time - start_elapsed_time
- print "" # make sure runTime is on its own line
- print runTime
+ print "" # make sure data is on its own line
+ if effectiveFrequencyInterface:
+ effectiveFrequency = effectiveFrequencyInterface.stop_sampling()
+ if effectiveFrequency > 0:
+ print "eftFreq: %s" % effectiveFrequency
+ runTime = stop_elapsed_time - start_elapsed_time
+ print "runTime: %s" % (runTime, )
sys.stdout.flush()
sys.exit(0)
12 years, 8 months