generic: test MADV_POPULATE_READ with IO errors
[xfstests-dev.git] / src / perf / FioCompare.py
1 # SPDX-License-Identifier: GPL-2.0
2
3 default_keys = [ 'iops', 'io_bytes', 'bw' ]
4 latency_keys = [ 'lat_ns_min', 'lat_ns_max' ]
5 main_job_keys = [ 'sys_cpu', 'elapsed' ]
6 io_ops = ['read', 'write', 'trim' ]
7
8 def _fuzzy_compare(a, b, fuzzy):
9     if a == b:
10         return 0
11     if a == 0:
12         return 100
13     a = float(a)
14     b = float(b)
15     fuzzy = float(fuzzy)
16     val = ((b - a) / a) * 100
17     if val > fuzzy or val < -fuzzy:
18         return val;
19     return 0
20
21 def _compare_jobs(ijob, njob, latency, fuzz, failures_only):
22     failed = 0
23     for k in default_keys:
24         for io in io_ops:
25             key = "{}_{}".format(io, k)
26             comp = _fuzzy_compare(ijob[key], njob[key], fuzz)
27             if comp < 0:
28                 print("    {} regressed: old {} new {} {}%".format(key,
29                       ijob[key], njob[key], comp))
30                 failed += 1
31             elif not failures_only and comp > 0:
32                 print("    {} improved: old {} new {} {}%".format(key,
33                       ijob[key], njob[key], comp))
34             elif not failures_only:
35                 print("{} is a-ok {} {}".format(key, ijob[key], njob[key]))
36     for k in latency_keys:
37         if not latency:
38             break
39         for io in io_ops:
40             key = "{}_{}".format(io, k)
41             comp = _fuzzy_compare(ijob[key], njob[key], fuzz)
42             if comp > 0:
43                 print("    {} regressed: old {} new {} {}%".format(key,
44                       ijob[key], njob[key], comp))
45                 failed += 1
46             elif not failures_only and comp < 0:
47                 print("    {} improved: old {} new {} {}%".format(key,
48                       ijob[key], njob[key], comp))
49             elif not failures_only:
50                 print("{} is a-ok {} {}".format(key, ijob[key], njob[key]))
51     for k in main_job_keys:
52         comp = _fuzzy_compare(ijob[k], njob[k], fuzz)
53         if comp > 0:
54             print("    {} regressed: old {} new {} {}%".format(k, ijob[k],
55                   njob[k], comp))
56             failed += 1
57         elif not failures_only and comp < 0:
58             print("    {} improved: old {} new {} {}%".format(k, ijob[k],
59                   njob[k], comp))
60         elif not failures_only:
61                 print("{} is a-ok {} {}".format(k, ijob[k], njob[k]))
62     return failed
63
64 def compare_individual_jobs(initial, data, fuzz, failures_only):
65     failed = 0;
66     initial_jobs = initial['jobs'][:]
67     for njob in data['jobs']:
68         for ijob in initial_jobs:
69             if njob['jobname'] == ijob['jobname']:
70                 print("  Checking results for {}".format(njob['jobname']))
71                 failed += _compare_jobs(ijob, njob, fuzz, failures_only)
72                 initial_jobs.remove(ijob)
73                 break
74     return failed
75
76 def default_merge(data):
77     '''Default merge function for multiple jobs in one run
78
79     For runs that include multiple threads we will have a lot of variation
80     between the different threads, which makes comparing them to eachother
81     across multiple runs less that useful.  Instead merge the jobs into a single
82     job.  This function does that by adding up 'iops', 'io_kbytes', and 'bw' for
83     read/write/trim in the merged job, and then taking the maximal values of the
84     latency numbers.
85     '''
86     merge_job = {}
87     for job in data['jobs']:
88         for k in main_job_keys:
89             if k not in merge_job:
90                 merge_job[k] = job[k]
91             else:
92                 merge_job[k] += job[k]
93         for io in io_ops:
94             for k in default_keys:
95                 key = "{}_{}".format(io, k)
96                 if key not in merge_job:
97                     merge_job[key] = job[key]
98                 else:
99                     merge_job[key] += job[key]
100             for k in latency_keys:
101                 key = "{}_{}".format(io, k)
102                 if key not in merge_job:
103                     merge_job[key] = job[key]
104                 elif merge_job[key] < job[key]:
105                     merge_job[key] = job[key]
106     return merge_job
107
108 def compare_fiodata(initial, data, latency, merge_func=default_merge, fuzz=5,
109                     failures_only=True):
110     failed  = 0
111     if merge_func is None:
112         return compare_individual_jobs(initial, data, fuzz, failures_only)
113     ijob = merge_func(initial)
114     njob = merge_func(data)
115     return _compare_jobs(ijob, njob, latency, fuzz, failures_only)