17db96d56Sopenharmony_ci"""Print a summary of specialization stats for all files in the 27db96d56Sopenharmony_cidefault stats folders. 37db96d56Sopenharmony_ci""" 47db96d56Sopenharmony_ci 57db96d56Sopenharmony_ciimport collections 67db96d56Sopenharmony_ciimport os.path 77db96d56Sopenharmony_ciimport opcode 87db96d56Sopenharmony_cifrom datetime import date 97db96d56Sopenharmony_ciimport itertools 107db96d56Sopenharmony_ciimport argparse 117db96d56Sopenharmony_ci 127db96d56Sopenharmony_ciif os.name == "nt": 137db96d56Sopenharmony_ci DEFAULT_DIR = "c:\\temp\\py_stats\\" 147db96d56Sopenharmony_cielse: 157db96d56Sopenharmony_ci DEFAULT_DIR = "/tmp/py_stats/" 167db96d56Sopenharmony_ci 177db96d56Sopenharmony_ci#Create list of all instruction names 187db96d56Sopenharmony_cispecialized = iter(opcode._specialized_instructions) 197db96d56Sopenharmony_ciopname = ["<0>"] 207db96d56Sopenharmony_cifor name in opcode.opname[1:]: 217db96d56Sopenharmony_ci if name.startswith("<"): 227db96d56Sopenharmony_ci try: 237db96d56Sopenharmony_ci name = next(specialized) 247db96d56Sopenharmony_ci except StopIteration: 257db96d56Sopenharmony_ci pass 267db96d56Sopenharmony_ci opname.append(name) 277db96d56Sopenharmony_ci 287db96d56Sopenharmony_ci# opcode_name --> opcode 297db96d56Sopenharmony_ci# Sort alphabetically. 307db96d56Sopenharmony_ciopmap = {name: i for i, name in enumerate(opname)} 317db96d56Sopenharmony_ciopmap = dict(sorted(opmap.items())) 327db96d56Sopenharmony_ci 337db96d56Sopenharmony_ciTOTAL = "specialization.deferred", "specialization.hit", "specialization.miss", "execution_count" 347db96d56Sopenharmony_ci 357db96d56Sopenharmony_cidef print_specialization_stats(name, family_stats, defines): 367db96d56Sopenharmony_ci if "specializable" not in family_stats: 377db96d56Sopenharmony_ci return 387db96d56Sopenharmony_ci total = sum(family_stats.get(kind, 0) for kind in TOTAL) 397db96d56Sopenharmony_ci if total == 0: 407db96d56Sopenharmony_ci return 417db96d56Sopenharmony_ci with Section(name, 3, f"specialization stats for {name} family"): 427db96d56Sopenharmony_ci rows = [] 437db96d56Sopenharmony_ci for key in sorted(family_stats): 447db96d56Sopenharmony_ci if key.startswith("specialization.failure_kinds"): 457db96d56Sopenharmony_ci continue 467db96d56Sopenharmony_ci if key in ("specialization.hit", "specialization.miss"): 477db96d56Sopenharmony_ci label = key[len("specialization."):] 487db96d56Sopenharmony_ci elif key == "execution_count": 497db96d56Sopenharmony_ci label = "unquickened" 507db96d56Sopenharmony_ci elif key in ("specialization.success", "specialization.failure", "specializable"): 517db96d56Sopenharmony_ci continue 527db96d56Sopenharmony_ci elif key.startswith("pair"): 537db96d56Sopenharmony_ci continue 547db96d56Sopenharmony_ci else: 557db96d56Sopenharmony_ci label = key 567db96d56Sopenharmony_ci rows.append((f"{label:>12}", f"{family_stats[key]:>12}", f"{100*family_stats[key]/total:0.1f}%")) 577db96d56Sopenharmony_ci emit_table(("Kind", "Count", "Ratio"), rows) 587db96d56Sopenharmony_ci print_title("Specialization attempts", 4) 597db96d56Sopenharmony_ci total_attempts = 0 607db96d56Sopenharmony_ci for key in ("specialization.success", "specialization.failure"): 617db96d56Sopenharmony_ci total_attempts += family_stats.get(key, 0) 627db96d56Sopenharmony_ci rows = [] 637db96d56Sopenharmony_ci for key in ("specialization.success", "specialization.failure"): 647db96d56Sopenharmony_ci label = key[len("specialization."):] 657db96d56Sopenharmony_ci label = label[0].upper() + label[1:] 667db96d56Sopenharmony_ci val = family_stats.get(key, 0) 677db96d56Sopenharmony_ci rows.append((label, val, f"{100*val/total_attempts:0.1f}%")) 687db96d56Sopenharmony_ci emit_table(("", "Count:", "Ratio:"), rows) 697db96d56Sopenharmony_ci total_failures = family_stats.get("specialization.failure", 0) 707db96d56Sopenharmony_ci failure_kinds = [ 0 ] * 30 717db96d56Sopenharmony_ci for key in family_stats: 727db96d56Sopenharmony_ci if not key.startswith("specialization.failure_kind"): 737db96d56Sopenharmony_ci continue 747db96d56Sopenharmony_ci _, index = key[:-1].split("[") 757db96d56Sopenharmony_ci index = int(index) 767db96d56Sopenharmony_ci failure_kinds[index] = family_stats[key] 777db96d56Sopenharmony_ci failures = [(value, index) for (index, value) in enumerate(failure_kinds)] 787db96d56Sopenharmony_ci failures.sort(reverse=True) 797db96d56Sopenharmony_ci rows = [] 807db96d56Sopenharmony_ci for value, index in failures: 817db96d56Sopenharmony_ci if not value: 827db96d56Sopenharmony_ci continue 837db96d56Sopenharmony_ci rows.append((kind_to_text(index, defines, name), value, f"{100*value/total_failures:0.1f}%")) 847db96d56Sopenharmony_ci emit_table(("Failure kind", "Count:", "Ratio:"), rows) 857db96d56Sopenharmony_ci 867db96d56Sopenharmony_cidef gather_stats(): 877db96d56Sopenharmony_ci stats = collections.Counter() 887db96d56Sopenharmony_ci for filename in os.listdir(DEFAULT_DIR): 897db96d56Sopenharmony_ci with open(os.path.join(DEFAULT_DIR, filename)) as fd: 907db96d56Sopenharmony_ci for line in fd: 917db96d56Sopenharmony_ci key, value = line.split(":") 927db96d56Sopenharmony_ci key = key.strip() 937db96d56Sopenharmony_ci value = int(value) 947db96d56Sopenharmony_ci stats[key] += value 957db96d56Sopenharmony_ci return stats 967db96d56Sopenharmony_ci 977db96d56Sopenharmony_cidef extract_opcode_stats(stats): 987db96d56Sopenharmony_ci opcode_stats = [ {} for _ in range(256) ] 997db96d56Sopenharmony_ci for key, value in stats.items(): 1007db96d56Sopenharmony_ci if not key.startswith("opcode"): 1017db96d56Sopenharmony_ci continue 1027db96d56Sopenharmony_ci n, _, rest = key[7:].partition("]") 1037db96d56Sopenharmony_ci opcode_stats[int(n)][rest.strip(".")] = value 1047db96d56Sopenharmony_ci return opcode_stats 1057db96d56Sopenharmony_ci 1067db96d56Sopenharmony_cidef parse_kinds(spec_src): 1077db96d56Sopenharmony_ci defines = collections.defaultdict(list) 1087db96d56Sopenharmony_ci for line in spec_src: 1097db96d56Sopenharmony_ci line = line.strip() 1107db96d56Sopenharmony_ci if not line.startswith("#define SPEC_FAIL_"): 1117db96d56Sopenharmony_ci continue 1127db96d56Sopenharmony_ci line = line[len("#define SPEC_FAIL_"):] 1137db96d56Sopenharmony_ci name, val = line.split() 1147db96d56Sopenharmony_ci defines[int(val.strip())].append(name.strip()) 1157db96d56Sopenharmony_ci return defines 1167db96d56Sopenharmony_ci 1177db96d56Sopenharmony_cidef pretty(defname): 1187db96d56Sopenharmony_ci return defname.replace("_", " ").lower() 1197db96d56Sopenharmony_ci 1207db96d56Sopenharmony_cidef kind_to_text(kind, defines, opname): 1217db96d56Sopenharmony_ci if kind < 7: 1227db96d56Sopenharmony_ci return pretty(defines[kind][0]) 1237db96d56Sopenharmony_ci if opname.endswith("ATTR"): 1247db96d56Sopenharmony_ci opname = "ATTR" 1257db96d56Sopenharmony_ci if opname.endswith("SUBSCR"): 1267db96d56Sopenharmony_ci opname = "SUBSCR" 1277db96d56Sopenharmony_ci if opname.startswith("PRECALL"): 1287db96d56Sopenharmony_ci opname = "CALL" 1297db96d56Sopenharmony_ci for name in defines[kind]: 1307db96d56Sopenharmony_ci if name.startswith(opname): 1317db96d56Sopenharmony_ci return pretty(name[len(opname)+1:]) 1327db96d56Sopenharmony_ci return "kind " + str(kind) 1337db96d56Sopenharmony_ci 1347db96d56Sopenharmony_cidef categorized_counts(opcode_stats): 1357db96d56Sopenharmony_ci basic = 0 1367db96d56Sopenharmony_ci specialized = 0 1377db96d56Sopenharmony_ci not_specialized = 0 1387db96d56Sopenharmony_ci specialized_instructions = { 1397db96d56Sopenharmony_ci op for op in opcode._specialized_instructions 1407db96d56Sopenharmony_ci if "__" not in op and "ADAPTIVE" not in op} 1417db96d56Sopenharmony_ci adaptive_instructions = { 1427db96d56Sopenharmony_ci op for op in opcode._specialized_instructions 1437db96d56Sopenharmony_ci if "ADAPTIVE" in op} 1447db96d56Sopenharmony_ci for i, opcode_stat in enumerate(opcode_stats): 1457db96d56Sopenharmony_ci if "execution_count" not in opcode_stat: 1467db96d56Sopenharmony_ci continue 1477db96d56Sopenharmony_ci count = opcode_stat['execution_count'] 1487db96d56Sopenharmony_ci name = opname[i] 1497db96d56Sopenharmony_ci if "specializable" in opcode_stat: 1507db96d56Sopenharmony_ci not_specialized += count 1517db96d56Sopenharmony_ci elif name in adaptive_instructions: 1527db96d56Sopenharmony_ci not_specialized += count 1537db96d56Sopenharmony_ci elif name in specialized_instructions: 1547db96d56Sopenharmony_ci miss = opcode_stat.get("specialization.miss", 0) 1557db96d56Sopenharmony_ci not_specialized += miss 1567db96d56Sopenharmony_ci specialized += count - miss 1577db96d56Sopenharmony_ci else: 1587db96d56Sopenharmony_ci basic += count 1597db96d56Sopenharmony_ci return basic, not_specialized, specialized 1607db96d56Sopenharmony_ci 1617db96d56Sopenharmony_cidef print_title(name, level=2): 1627db96d56Sopenharmony_ci print("#"*level, name) 1637db96d56Sopenharmony_ci print() 1647db96d56Sopenharmony_ci 1657db96d56Sopenharmony_ciclass Section: 1667db96d56Sopenharmony_ci 1677db96d56Sopenharmony_ci def __init__(self, title, level=2, summary=None): 1687db96d56Sopenharmony_ci self.title = title 1697db96d56Sopenharmony_ci self.level = level 1707db96d56Sopenharmony_ci if summary is None: 1717db96d56Sopenharmony_ci self.summary = title.lower() 1727db96d56Sopenharmony_ci else: 1737db96d56Sopenharmony_ci self.summary = summary 1747db96d56Sopenharmony_ci 1757db96d56Sopenharmony_ci def __enter__(self): 1767db96d56Sopenharmony_ci print_title(self.title, self.level) 1777db96d56Sopenharmony_ci print("<details>") 1787db96d56Sopenharmony_ci print("<summary>", self.summary, "</summary>") 1797db96d56Sopenharmony_ci print() 1807db96d56Sopenharmony_ci return self 1817db96d56Sopenharmony_ci 1827db96d56Sopenharmony_ci def __exit__(*args): 1837db96d56Sopenharmony_ci print() 1847db96d56Sopenharmony_ci print("</details>") 1857db96d56Sopenharmony_ci print() 1867db96d56Sopenharmony_ci 1877db96d56Sopenharmony_cidef emit_table(header, rows): 1887db96d56Sopenharmony_ci width = len(header) 1897db96d56Sopenharmony_ci header_line = "|" 1907db96d56Sopenharmony_ci under_line = "|" 1917db96d56Sopenharmony_ci for item in header: 1927db96d56Sopenharmony_ci under = "---" 1937db96d56Sopenharmony_ci if item.endswith(":"): 1947db96d56Sopenharmony_ci item = item[:-1] 1957db96d56Sopenharmony_ci under += ":" 1967db96d56Sopenharmony_ci header_line += item + " | " 1977db96d56Sopenharmony_ci under_line += under + "|" 1987db96d56Sopenharmony_ci print(header_line) 1997db96d56Sopenharmony_ci print(under_line) 2007db96d56Sopenharmony_ci for row in rows: 2017db96d56Sopenharmony_ci if width is not None and len(row) != width: 2027db96d56Sopenharmony_ci raise ValueError("Wrong number of elements in row '" + str(rows) + "'") 2037db96d56Sopenharmony_ci print("|", " | ".join(str(i) for i in row), "|") 2047db96d56Sopenharmony_ci print() 2057db96d56Sopenharmony_ci 2067db96d56Sopenharmony_cidef emit_execution_counts(opcode_stats, total): 2077db96d56Sopenharmony_ci with Section("Execution counts", summary="execution counts for all instructions"): 2087db96d56Sopenharmony_ci counts = [] 2097db96d56Sopenharmony_ci for i, opcode_stat in enumerate(opcode_stats): 2107db96d56Sopenharmony_ci if "execution_count" in opcode_stat: 2117db96d56Sopenharmony_ci count = opcode_stat['execution_count'] 2127db96d56Sopenharmony_ci miss = 0 2137db96d56Sopenharmony_ci if "specializable" not in opcode_stat: 2147db96d56Sopenharmony_ci miss = opcode_stat.get("specialization.miss") 2157db96d56Sopenharmony_ci counts.append((count, opname[i], miss)) 2167db96d56Sopenharmony_ci counts.sort(reverse=True) 2177db96d56Sopenharmony_ci cumulative = 0 2187db96d56Sopenharmony_ci rows = [] 2197db96d56Sopenharmony_ci for (count, name, miss) in counts: 2207db96d56Sopenharmony_ci cumulative += count 2217db96d56Sopenharmony_ci if miss: 2227db96d56Sopenharmony_ci miss = f"{100*miss/count:0.1f}%" 2237db96d56Sopenharmony_ci else: 2247db96d56Sopenharmony_ci miss = "" 2257db96d56Sopenharmony_ci rows.append((name, count, f"{100*count/total:0.1f}%", 2267db96d56Sopenharmony_ci f"{100*cumulative/total:0.1f}%", miss)) 2277db96d56Sopenharmony_ci emit_table( 2287db96d56Sopenharmony_ci ("Name", "Count:", "Self:", "Cumulative:", "Miss ratio:"), 2297db96d56Sopenharmony_ci rows 2307db96d56Sopenharmony_ci ) 2317db96d56Sopenharmony_ci 2327db96d56Sopenharmony_ci 2337db96d56Sopenharmony_cidef emit_specialization_stats(opcode_stats): 2347db96d56Sopenharmony_ci spec_path = os.path.join(os.path.dirname(__file__), "../../Python/specialize.c") 2357db96d56Sopenharmony_ci with open(spec_path) as spec_src: 2367db96d56Sopenharmony_ci defines = parse_kinds(spec_src) 2377db96d56Sopenharmony_ci with Section("Specialization stats", summary="specialization stats by family"): 2387db96d56Sopenharmony_ci for i, opcode_stat in enumerate(opcode_stats): 2397db96d56Sopenharmony_ci name = opname[i] 2407db96d56Sopenharmony_ci print_specialization_stats(name, opcode_stat, defines) 2417db96d56Sopenharmony_ci 2427db96d56Sopenharmony_cidef emit_specialization_overview(opcode_stats, total): 2437db96d56Sopenharmony_ci basic, not_specialized, specialized = categorized_counts(opcode_stats) 2447db96d56Sopenharmony_ci with Section("Specialization effectiveness"): 2457db96d56Sopenharmony_ci emit_table(("Instructions", "Count:", "Ratio:"), ( 2467db96d56Sopenharmony_ci ("Basic", basic, f"{basic*100/total:0.1f}%"), 2477db96d56Sopenharmony_ci ("Not specialized", not_specialized, f"{not_specialized*100/total:0.1f}%"), 2487db96d56Sopenharmony_ci ("Specialized", specialized, f"{specialized*100/total:0.1f}%"), 2497db96d56Sopenharmony_ci )) 2507db96d56Sopenharmony_ci 2517db96d56Sopenharmony_cidef emit_call_stats(stats): 2527db96d56Sopenharmony_ci with Section("Call stats", summary="Inlined calls and frame stats"): 2537db96d56Sopenharmony_ci total = 0 2547db96d56Sopenharmony_ci for key, value in stats.items(): 2557db96d56Sopenharmony_ci if "Calls to" in key: 2567db96d56Sopenharmony_ci total += value 2577db96d56Sopenharmony_ci rows = [] 2587db96d56Sopenharmony_ci for key, value in stats.items(): 2597db96d56Sopenharmony_ci if "Calls to" in key: 2607db96d56Sopenharmony_ci rows.append((key, value, f"{100*value/total:0.1f}%")) 2617db96d56Sopenharmony_ci for key, value in stats.items(): 2627db96d56Sopenharmony_ci if key.startswith("Frame"): 2637db96d56Sopenharmony_ci rows.append((key, value, f"{100*value/total:0.1f}%")) 2647db96d56Sopenharmony_ci emit_table(("", "Count:", "Ratio:"), rows) 2657db96d56Sopenharmony_ci 2667db96d56Sopenharmony_cidef emit_object_stats(stats): 2677db96d56Sopenharmony_ci with Section("Object stats", summary="allocations, frees and dict materializatons"): 2687db96d56Sopenharmony_ci total = stats.get("Object new values") 2697db96d56Sopenharmony_ci rows = [] 2707db96d56Sopenharmony_ci for key, value in stats.items(): 2717db96d56Sopenharmony_ci if key.startswith("Object"): 2727db96d56Sopenharmony_ci if "materialize" in key: 2737db96d56Sopenharmony_ci materialize = f"{100*value/total:0.1f}%" 2747db96d56Sopenharmony_ci else: 2757db96d56Sopenharmony_ci materialize = "" 2767db96d56Sopenharmony_ci label = key[6:].strip() 2777db96d56Sopenharmony_ci label = label[0].upper() + label[1:] 2787db96d56Sopenharmony_ci rows.append((label, value, materialize)) 2797db96d56Sopenharmony_ci emit_table(("", "Count:", "Ratio:"), rows) 2807db96d56Sopenharmony_ci 2817db96d56Sopenharmony_cidef get_total(opcode_stats): 2827db96d56Sopenharmony_ci total = 0 2837db96d56Sopenharmony_ci for opcode_stat in opcode_stats: 2847db96d56Sopenharmony_ci if "execution_count" in opcode_stat: 2857db96d56Sopenharmony_ci total += opcode_stat['execution_count'] 2867db96d56Sopenharmony_ci return total 2877db96d56Sopenharmony_ci 2887db96d56Sopenharmony_cidef emit_pair_counts(opcode_stats, total): 2897db96d56Sopenharmony_ci pair_counts = [] 2907db96d56Sopenharmony_ci for i, opcode_stat in enumerate(opcode_stats): 2917db96d56Sopenharmony_ci if i == 0: 2927db96d56Sopenharmony_ci continue 2937db96d56Sopenharmony_ci for key, value in opcode_stat.items(): 2947db96d56Sopenharmony_ci if key.startswith("pair_count"): 2957db96d56Sopenharmony_ci x, _, _ = key[11:].partition("]") 2967db96d56Sopenharmony_ci if value: 2977db96d56Sopenharmony_ci pair_counts.append((value, (i, int(x)))) 2987db96d56Sopenharmony_ci with Section("Pair counts", summary="Pair counts for top 100 pairs"): 2997db96d56Sopenharmony_ci pair_counts.sort(reverse=True) 3007db96d56Sopenharmony_ci cumulative = 0 3017db96d56Sopenharmony_ci rows = [] 3027db96d56Sopenharmony_ci for (count, pair) in itertools.islice(pair_counts, 100): 3037db96d56Sopenharmony_ci i, j = pair 3047db96d56Sopenharmony_ci cumulative += count 3057db96d56Sopenharmony_ci rows.append((opname[i] + " " + opname[j], count, f"{100*count/total:0.1f}%", 3067db96d56Sopenharmony_ci f"{100*cumulative/total:0.1f}%")) 3077db96d56Sopenharmony_ci emit_table(("Pair", "Count:", "Self:", "Cumulative:"), 3087db96d56Sopenharmony_ci rows 3097db96d56Sopenharmony_ci ) 3107db96d56Sopenharmony_ci with Section("Predecessor/Successor Pairs", summary="Top 3 predecessors and successors of each opcode"): 3117db96d56Sopenharmony_ci predecessors = collections.defaultdict(collections.Counter) 3127db96d56Sopenharmony_ci successors = collections.defaultdict(collections.Counter) 3137db96d56Sopenharmony_ci total_predecessors = collections.Counter() 3147db96d56Sopenharmony_ci total_successors = collections.Counter() 3157db96d56Sopenharmony_ci for count, (first, second) in pair_counts: 3167db96d56Sopenharmony_ci if count: 3177db96d56Sopenharmony_ci predecessors[second][first] = count 3187db96d56Sopenharmony_ci successors[first][second] = count 3197db96d56Sopenharmony_ci total_predecessors[second] += count 3207db96d56Sopenharmony_ci total_successors[first] += count 3217db96d56Sopenharmony_ci for name, i in opmap.items(): 3227db96d56Sopenharmony_ci total1 = total_predecessors[i] 3237db96d56Sopenharmony_ci total2 = total_successors[i] 3247db96d56Sopenharmony_ci if total1 == 0 and total2 == 0: 3257db96d56Sopenharmony_ci continue 3267db96d56Sopenharmony_ci pred_rows = succ_rows = () 3277db96d56Sopenharmony_ci if total1: 3287db96d56Sopenharmony_ci pred_rows = [(opname[pred], count, f"{count/total1:.1%}") 3297db96d56Sopenharmony_ci for (pred, count) in predecessors[i].most_common(3)] 3307db96d56Sopenharmony_ci if total2: 3317db96d56Sopenharmony_ci succ_rows = [(opname[succ], count, f"{count/total2:.1%}") 3327db96d56Sopenharmony_ci for (succ, count) in successors[i].most_common(3)] 3337db96d56Sopenharmony_ci with Section(name, 3, f"Successors and predecessors for {name}"): 3347db96d56Sopenharmony_ci emit_table(("Predecessors", "Count:", "Percentage:"), 3357db96d56Sopenharmony_ci pred_rows 3367db96d56Sopenharmony_ci ) 3377db96d56Sopenharmony_ci emit_table(("Successors", "Count:", "Percentage:"), 3387db96d56Sopenharmony_ci succ_rows 3397db96d56Sopenharmony_ci ) 3407db96d56Sopenharmony_ci 3417db96d56Sopenharmony_cidef main(): 3427db96d56Sopenharmony_ci stats = gather_stats() 3437db96d56Sopenharmony_ci opcode_stats = extract_opcode_stats(stats) 3447db96d56Sopenharmony_ci total = get_total(opcode_stats) 3457db96d56Sopenharmony_ci emit_execution_counts(opcode_stats, total) 3467db96d56Sopenharmony_ci emit_pair_counts(opcode_stats, total) 3477db96d56Sopenharmony_ci emit_specialization_stats(opcode_stats) 3487db96d56Sopenharmony_ci emit_specialization_overview(opcode_stats, total) 3497db96d56Sopenharmony_ci emit_call_stats(stats) 3507db96d56Sopenharmony_ci emit_object_stats(stats) 3517db96d56Sopenharmony_ci print("---") 3527db96d56Sopenharmony_ci print("Stats gathered on:", date.today()) 3537db96d56Sopenharmony_ci 3547db96d56Sopenharmony_ciif __name__ == "__main__": 3557db96d56Sopenharmony_ci main() 356