report.py 5.7 KB
Newer Older
1
#!/usr/bin/env python3
2 3

import re
4
import argparse
5

6

7 8 9 10 11 12
def get_results(filename):
    file = open(filename, "r")
    lines = file.read().split('\n')

    results = {}

13
    re_match = re.compile(r"(\S+) - (\S+ \S+) shader: (\S*) inst, (\S*) loops, (\S*) cycles, (\S*):(\S*) spills:fills")
14 15
    for line in lines:
        match = re.search(re_match, line)
Dylan Baker's avatar
Dylan Baker committed
16
        if match is None:
17 18 19
            continue

        groups = match.groups()
20 21 22
        inst_count = int(groups[2])
        loop_count = int(groups[3])
        cycle_count = int(groups[4])
23 24
        spill_count = int(groups[5])
        fill_count = int(groups[6])
25 26 27
        if inst_count != 0:
            results[(groups[0], groups[1])] = {
                "instructions": inst_count,
28 29
                "spills": spill_count,
                "fills": fill_count,
30 31 32
                "cycles": cycle_count,
                "loops": loop_count
            }
33 34 35

    return results

36

37 38 39 40 41 42 43 44
def format_percent(frac):
    """Converts a factional value (typically 0.0 to 1.0) to a string as a percentage"""
    if abs(frac) > 0.0 and abs(frac) < 0.0001:
        return "<.01%"
    else:
        return "{:.2f}%".format(frac * 100)


45 46 47
def get_delta(b, a):
    if b != 0 and a != 0:
        frac = float(a) / float(b) - 1.0
48
        return ' ({})'.format(format_percent(frac))
49 50 51
    else:
        return ''

52

53 54 55
def change(b, a):
    return str(b) + " -> " + str(a) + get_delta(b, a)

56

57 58 59 60 61 62
def get_result_string(p, b, a):
    p = p + ": "
    while len(p) < 50:
        p = p + ' '
    return p + change(b, a)

63 64
def split_list(string):
    return string.split(",")
65

66
def main():
67
    parser = argparse.ArgumentParser()
68
    parser.add_argument("--measurements", "-m", type=split_list,
69
                        default=["instructions", "cycles", "loops", "spills", "fills"],
70
                        help="comma-separated list of measurements to report")
71 72
    parser.add_argument("--summary-only", "-s", action="store_true", default=False,
                        help="do not show the per-shader helped / hurt data")
73 74
    parser.add_argument("--changes-only", "-c", action="store_true", default=False,
                        help="only show measurements that have changes")
75 76
    parser.add_argument("before", type=get_results, help="the output of the original code")
    parser.add_argument("after", type=get_results, help="the output of the new code")
77
    args = parser.parse_args()
78

79 80 81 82 83 84
    total_before = {}
    total_after = {}
    affected_before = {}
    affected_after = {}
    num_hurt = {}
    num_helped = {}
85

86 87 88 89 90 91 92 93 94 95 96 97 98
    for m in args.measurements:
        total_before[m] = 0
        total_after[m] = 0
        affected_before[m] = 0
        affected_after[m] = 0

        helped = []
        hurt = []
        for p in args.before:
            before_count = args.before[p][m]

            if args.after.get(p) is None:
                continue
99

100 101 102 103
            # If the number of loops changed, then we may have unrolled some
            # loops, in which case other measurements will be misleading.
            if m != "loops" and args.before[p]["loops"] != args.after[p]["loops"]:
                continue
104

105
            after_count = args.after[p][m]
106

107 108
            total_before[m] += before_count
            total_after[m] += after_count
109 110

            if before_count != after_count:
111 112
                affected_before[m] += before_count
                affected_after[m] += after_count
113

114
                if after_count > before_count:
115 116
                    hurt.append(p)
                else:
117
                    helped.append(p)
118

119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
        if not args.summary_only:
            helped.sort(
                key=lambda k: args.after[k][m] if args.before[k][m] == 0 else float(args.before[k][m] - args.after[k][m]) / args.before[k][m])
            for p in helped:
                namestr = p[0] + " " + p[1]
                print(m + " helped:   " + get_result_string(
                    namestr, args.before[p][m], args.after[p][m]))
            if helped:
                print("")

            hurt.sort(
                key=lambda k: args.after[k][m] if args.before[k][m] == 0 else float(args.after[k][m] - args.before[k][m]) / args.before[k][m])
            for p in hurt:
                namestr = p[0] + " " + p[1]
                print(m + " HURT:   " + get_result_string(
                    namestr, args.before[p][m], args.after[p][m]))
            if hurt:
                print("")
137

138 139
        num_helped[m] = len(helped)
        num_hurt[m] = len(hurt)
140

141 142 143 144 145 146 147 148 149 150 151

    lost = []
    gained = []

    for p in args.before:
        if args.after.get(p) is None:
            lost.append(p[0] + " " + p[1])

    for p in args.after:
        if args.before.get(p) is None:
            gained.append(p[0] + " " + p[1])
152

153 154 155 156 157 158 159 160 161 162 163 164
    if not args.summary_only:
        lost.sort()
        for p in lost:
            print("LOST:   " + p)
        if lost:
            print("")

        gained.sort()
        for p in gained:
            print("GAINED: " + p)
        if gained:
            print("")
165

166
    any_helped_or_hurt = False
167
    for m in args.measurements:
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
        if num_helped[m] > 0 or num_hurt[m] > 0:
            any_helped_or_hurt = True

        if num_helped[m] > 0 or num_hurt[m] > 0 or not args.changes_only:
            print("total {0} in shared programs: {1}\n"
                  "{0} in affected programs: {2}\n"
                  "helped: {3}\n"
                  "HURT: {4}\n".format(
	              m,
	              change(total_before[m], total_after[m]),
	              change(affected_before[m], affected_after[m]),
	              num_helped[m],
	              num_hurt[m]))


    if lost or gained or not args.changes_only:
        print("LOST:   " + str(len(lost)))
        print("GAINED: " + str(len(gained)))
    else:
        if not any_helped_or_hurt:
            print("No changes.")
189

190
if __name__ == "__main__":
Dylan Baker's avatar
Dylan Baker committed
191
    main()