kvm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Amos Kong <akong@redhat.com>
To: lmr@redhat.com, autotest@test.kernel.org
Cc: kvm@vger.kernel.org
Subject: [Autotest PATCH v2 4/4] virt: Introduce regression testing	infrastructure
Date: Thu, 05 Jan 2012 11:06:23 +0800	[thread overview]
Message-ID: <20120105030623.8563.7790.stgit@dhcp-8-167.nay.redhat.com> (raw)
In-Reply-To: <20120105030456.8563.65512.stgit@dhcp-8-167.nay.redhat.com>

> regression.py:
 'regression' module is used to compare the test results
 of two jobs, we can use it (regression.compare()) at
 the end of control file,

This script can also be used directly. Example(tested in unclear env):
| # python regression.py netperf /result1-dir /result2-dir \
|   ../../tests/kvm/perf.conf

> analyzer.py:
 It's used to compute average, standard deviation, augment rate, etc,
 and compare two test results (standard format).
 it can be called at the end of job (end of control file), we can
 also be used directly.
| # python analyzer.py "result-v1-1.RHS result-v1-2.RHS" \
|   "result-v2-1.RHS result-v2-2.RHS result-v2-3.RHS" log.txt
| Thu Jan  5 10:17:24 2012
|
| == Avg1 SD Augment Rate ==========================
|     TCP_STREAM
|     size|sessions|throughput|   cpu|normalize| ...
|     2048|       2|  14699.17| 31.73|   463.19| ...
| %SD     |     0.0|       0.6|   0.0|      0.8| ...
|     2048|       4|  15935.68| 34.30|   464.66| ...
| %SD     |     0.0|       0.3|   1.7|      1.5| ...
| ...
|
| == AvgS Augment Rate =============================
|     TCP_STREAM
|     size|sessions|throughput|   cpu|normalize| ...
|     2048|       2|   7835.61| 31.66|   247.36| ...
|     2048|       2|   8757.03| 31.94|   274.14| ...
| %       |    +0.0|     +11.8|  +0.9|    +10.8| ...
|     2048|       4|  12000.65| 32.38|   370.62| ...
|     2048|       4|  13641.20| 32.27|   423.29| ...
| %       |    +0.0|     +13.7|  -0.3|    +14.2| ...
|

> perf.conf:
 config test related parameters.

It supports to compare current result with the result in autotest server.
autotest result directory should be shared by NFS first, and specify
its address in perf.conf

Changes from v1:
- refactor analysis code
- add standard deviation percent

Signed-off-by: Amos Kong <akong@redhat.com>
---
 client/tests/kvm/control        |    7 ++
 client/tests/kvm/perf.conf      |   23 +++++
 client/virt/tests/analyzer.py   |  172 +++++++++++++++++++++++++++++++++++++++
 client/virt/tests/regression.py |   34 ++++++++
 4 files changed, 236 insertions(+), 0 deletions(-)
 create mode 100644 client/tests/kvm/perf.conf
 create mode 100644 client/virt/tests/analyzer.py
 create mode 100644 client/virt/tests/regression.py

diff --git a/client/tests/kvm/control b/client/tests/kvm/control
index 950154c..5f4df87 100644
--- a/client/tests/kvm/control
+++ b/client/tests/kvm/control
@@ -67,3 +67,10 @@ if args:
 parser.parse_string(str)
 
 virt_utils.run_tests(parser, job)
+
+# compare the perfmance results of job
+# from autotest_lib.client.virt.tests import regression
+# regression.compare("ntttcp", "$olddir",
+# "%s/results/default/" % os.environ['AUTODIR'],
+# config_file="%s/tests/kvm/perf.conf" % os.environ['AUTODIR'],
+# output_dir="%s/results/default/netperf-result.txt" % os.environ['AUTODIR'])
diff --git a/client/tests/kvm/perf.conf b/client/tests/kvm/perf.conf
new file mode 100644
index 0000000..31b72b2
--- /dev/null
+++ b/client/tests/kvm/perf.conf
@@ -0,0 +1,23 @@
+# this config file is used to set test related parameters
+#
+
+[server]
+result_nfs = kvm-autotest.englab.nay.redhat.com:/usr/local/autotest/results
+result_mntdir = /results/
+
+[ntttcp]
+result_dir = results
+result_file_pattern = .*.RHS
+
+[netperf]
+result_dir = results
+result_file_pattern = netperf-result.RHS
+
+[iozone]
+result_dir = guest_test_results
+result_file_pattern =
+
+[ffsb]
+result_dir = results
+result_file_pattern =
+
diff --git a/client/virt/tests/analyzer.py b/client/virt/tests/analyzer.py
new file mode 100644
index 0000000..24b13a3
--- /dev/null
+++ b/client/virt/tests/analyzer.py
@@ -0,0 +1,172 @@
+import sys, re, string, time, commands, os, random
+
+def tee(content, filename):
+    """ Write content to standard output and file """
+    fd = open(filename, "a")
+    fd.write(content + "\n")
+    fd.close()
+    print content
+
+class samples():
+    def __init__(self, files):
+        self.files_dict = []
+        for i in range(len(files)):
+            fd = open(files[i], "r")
+            self.files_dict.append(fd.readlines())
+            fd.close()
+
+    def getAvg(self):
+        return self._process(self.files_dict, self._get_list_avg)
+
+    def getAvgPercent(self, avgs_dict):
+        return self._process(avgs_dict, self._get_augment_rate)
+
+    def getSD(self):
+        return self._process(self.files_dict, self._get_list_sd)
+
+    def getSDPercent(self, sds_dict):
+        return self._process(sds_dict, self._get_percent)
+
+    def _get_percent(self, data):
+        """ num2 / num1 * 100 """
+        result = "0.0"
+        if len(data) == 2 and float(data[0]) != 0:
+            result = "%.1f" % (float(data[1]) / float(data[0]) * 100)
+        return result
+
+    def _get_augment_rate(self, data):
+        """ (num2 - num1) / num1 * 100 """
+        result = "+0.0"
+        if len(data) == 2 and float(data[0]) != 0:
+            result = "%+.1f" % \
+            (((float(data[1]) - float(data[0])) / float(data[0])) * 100)
+        return result
+
+    def _get_list_sd(self, data):
+        """
+        sumX = x1 + x2 + ... + xn
+        avgX = sumX / n
+        sumSquareX = x1^2 + ... + xn^2
+        SD = sqrt([sumSquareX - (n * (avgX ^ 2))] / (n - 1))
+        """
+        sum = sqsum = 0
+        n = len(data)
+        for i in data:
+            sum += float(i)
+            sqsum += float(i) ** 2
+        avg = sum / n
+        if avg == 0:
+            return "0.0"
+        return "%.1f" % (((sqsum - (n * avg**2)) / (n - 1))**0.5)
+
+    def _get_list_avg(self, data):
+        """ Compute the average of list members """
+        sum = 0
+        for i in data:
+            sum += float(i)
+        if "." in data[0]:
+            return "%.2f" % (sum / len(data))
+        return "%d" % (sum / len(data))
+
+    def _process_lines(self, files_dict, row, func):
+        """ Process lines of different sample files with assigned method """
+        lines = []
+        ret_lines = []
+
+        for i in range(len(files_dict)):
+            lines.append(files_dict[i][row].split("|"))
+        for col in range(len(lines[0])):
+            data_list = []
+            for i in range(len(lines)):
+                data_list.append(lines[i][col].strip())
+            ret_lines.append(func(data_list))
+        return "|".join(ret_lines)
+
+    def _process(self, files_dict, func):
+        """ Process dicts of sample files with assigned method """
+        ret_lines = []
+        if len(files_dict) == 1:
+            return files_dict[0], files_dict[0]
+        for i in range(len(files_dict[0])):
+            is_diff = False
+            for j in range(len(files_dict)):
+                if files_dict[0][i] != files_dict[j][i]:
+                    is_diff = True
+            if is_diff:
+                line = self._process_lines(files_dict, i, func)
+                ret_lines.append(line)
+            else:
+                ret_lines.append(files_dict[0][i].strip())
+        return ret_lines
+
+
+def display(lists, rate, f, summary="Augment Rate", prefix="% ", ignore_col=1):
+    """
+    Display lists data to standard format
+
+    param lists: row data lists
+    param rate: argument rate list
+    param f: result output file
+    param summary: compare result summary
+    param prefix: output prefix in rate lines
+    param ignore_col: do not display some columns
+    """
+    def format(list, str, ignore_col=0):
+        """ Format the string width of list member """
+        str = str.split("|")
+        for l in range(len(list)):
+            line = list[l].split("|")
+            for col in range(len(line)):
+                line[col] = line[col].rjust(len(str[col]), ' ')
+                if not re.findall("[a-zA-Z]", line[col]) and col < ignore_col:
+                    line[col] = " " * len(str[col])
+            list[l] = "|".join(line)
+        return list
+
+    for l in range(len(lists[0])):
+        if not re.findall("[a-zA-Z]", lists[0][l]):
+            break
+    tee("\n== %s " % summary + "="*(len(lists[0][l-1]) - len(summary) + 3) , f)
+    for n in range(len(lists)):
+        lists[n] = format(lists[n], lists[n][l-1])
+    rate = format(rate, rate[l-1], ignore_col)
+    for i in range(len(lists[0])):
+        for n in range(len(lists)):
+            is_diff = False
+            for j in range(len(lists)):
+                if lists[0][i] != lists[j][i]:
+                    is_diff = True
+            if is_diff or n==0:
+                tee(' ' * len(prefix) + lists[n][i], f)
+        if lists[0][i] != rate[i] and not re.findall("[a-zA-Z]", rate[i]):
+            tee(prefix + rate[i], f)
+
+
+def analyze(sample_list1, sample_list2, log_file="./netperf-result.txt"):
+    """ Compute averages of two lists of files, compare and display results """
+
+    commands.getoutput("rm -f %s" % log_file)
+    tee(time.ctime(time.time()), log_file)
+    s1 = samples(sample_list1.split())
+    avg1 = s1.getAvg()
+    sd1 = s1.getSD()
+    s2 = samples(sample_list2.split())
+    avg2 = s2.getAvg()
+    sd2 = s2.getSD()
+    sd1 = s1.getSDPercent([avg1, sd1])
+    sd2 = s1.getSDPercent([avg2, sd2])
+    display([avg1], sd1, log_file, summary="Avg1 SD Augment Rate",
+                   prefix="%SD ")
+    display([avg2], sd2, log_file, summary="Avg2 SD Augment Rate",
+                   prefix="%SD ")
+    avgs_rate = s1.getAvgPercent([avg1, avg2])
+    display([avg1, avg2], avgs_rate, log_file, summary="AvgS Augment Rate",
+                   prefix="%   ")
+
+
+if __name__ == "__main__":
+    if len(sys.argv) < 3:
+        print 'Usage: python %s "$results list1" "$results list2" $log_file'\
+              % sys.argv[0]
+        sys.exit(1)
+    analyze(sys.argv[1], sys.argv[2], sys.argv[3])
diff --git a/client/virt/tests/regression.py b/client/virt/tests/regression.py
new file mode 100644
index 0000000..4809554
--- /dev/null
+++ b/client/virt/tests/regression.py
@@ -0,0 +1,34 @@
+import ConfigParser, sys, commands, os
+import analyzer
+
+def compare(testname, olddir, curdir, config_file='perf.conf', output_dir=""):
+    config = ConfigParser.ConfigParser()
+    config.read(config_file)
+
+    result_nfs = config.get("server", "result_nfs")
+    result_mntdir = config.get("server", "result_mntdir")
+    result_dir = config.get(testname, "result_dir")
+    result_file_pattern = config.get(testname, "result_file_pattern")
+
+    def search_files(dir):
+        cmd = 'find %s|grep %s|grep "%s/%s"' % (dir,
+           testname, result_dir, result_file_pattern)
+        return commands.getoutput(cmd)
+
+    if not os.path.isdir(result_mntdir):
+        os.mkdir(result_mntdir)
+    commands.getoutput("mount %s %s" % (result_nfs, result_mntdir))
+
+    if not os.path.isabs(olddir):
+        olddir = result_mntdir + olddir
+    oldlist = search_files(olddir)
+    newlist = search_files(curdir)
+    if oldlist != "" or newlist != "":
+        analyzer.analyze(oldlist, newlist, output_dir)
+
+
+if __name__ == "__main__":
+    if len(sys.argv) != 5:
+        print 'Usage: python %s $testname $dir1 $dir2 $configfile' % sys.argv[0]
+        sys.exit(1)
+    compare(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])

  parent reply	other threads:[~2012-01-05  3:06 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-12-23 10:28 [RFC PATCH 0/4] Network performance regression Amos Kong
2011-12-23 10:28 ` [RFC PATCH 1/4] virt-test: add NTttcp subtests Amos Kong
2011-12-23 10:28 ` [RFC PATCH 2/4] virt-test: Refactor netperf test and add analysis module Amos Kong
2011-12-23 10:28 ` [RFC PATCH 3/4] netperf: pin guest vcpus/memory/vhost thread to numa node Amos Kong
2011-12-23 10:28 ` [RFC PATCH 4/4] virt: Introduce regression testing infrastructure Amos Kong
2011-12-24  1:13   ` Yang Hamo Bai
2011-12-25  1:26     ` Amos Kong
2011-12-29 13:12 ` [RFC PATCH 0/4] Network performance regression Amos Kong
2012-01-05  3:05 ` [Autotest PATCH v2 " Amos Kong
2012-01-05  3:05   ` [Autotest PATCH v2 1/4] virt-test: add NTttcp subtests Amos Kong
2012-01-05  3:06   ` [Autotest PATCH v2 2/4] virt-test: Refactor netperf test and add analysis module Amos Kong
2012-01-05  3:06   ` [Autotest PATCH v2 3/4] netperf: pin guest vcpus/memory/vhost thread to numa node Amos Kong
2012-01-05  3:06   ` Amos Kong [this message]
2012-01-06 20:17   ` [Autotest PATCH v2 0/4] Network performance regression Lucas Meneghel Rodrigues

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20120105030623.8563.7790.stgit@dhcp-8-167.nay.redhat.com \
    --to=akong@redhat.com \
    --cc=autotest@test.kernel.org \
    --cc=kvm@vger.kernel.org \
    --cc=lmr@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).