mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
GH-109330: Dump and compare stats using opcode names, not numbers (GH-109335)
This commit is contained in:
parent
90cf345ed4
commit
5dcbbd8861
2 changed files with 35 additions and 53 deletions
|
@ -123,7 +123,7 @@ _Py_GetSpecializationStats(void) {
|
||||||
|
|
||||||
#define PRINT_STAT(i, field) \
|
#define PRINT_STAT(i, field) \
|
||||||
if (stats[i].field) { \
|
if (stats[i].field) { \
|
||||||
fprintf(out, " opcode[%d]." #field " : %" PRIu64 "\n", i, stats[i].field); \
|
fprintf(out, " opcode[%s]." #field " : %" PRIu64 "\n", _PyOpcode_OpName[i], stats[i].field); \
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -131,11 +131,11 @@ print_spec_stats(FILE *out, OpcodeStats *stats)
|
||||||
{
|
{
|
||||||
/* Mark some opcodes as specializable for stats,
|
/* Mark some opcodes as specializable for stats,
|
||||||
* even though we don't specialize them yet. */
|
* even though we don't specialize them yet. */
|
||||||
fprintf(out, "opcode[%d].specializable : 1\n", BINARY_SLICE);
|
fprintf(out, "opcode[BINARY_SLICE].specializable : 1\n");
|
||||||
fprintf(out, "opcode[%d].specializable : 1\n", STORE_SLICE);
|
fprintf(out, "opcode[STORE_SLICE].specializable : 1\n");
|
||||||
for (int i = 0; i < 256; i++) {
|
for (int i = 0; i < 256; i++) {
|
||||||
if (_PyOpcode_Caches[i]) {
|
if (_PyOpcode_Caches[i]) {
|
||||||
fprintf(out, "opcode[%d].specializable : 1\n", i);
|
fprintf(out, "opcode[%s].specializable : 1\n", _PyOpcode_OpName[i]);
|
||||||
}
|
}
|
||||||
PRINT_STAT(i, specialization.success);
|
PRINT_STAT(i, specialization.success);
|
||||||
PRINT_STAT(i, specialization.failure);
|
PRINT_STAT(i, specialization.failure);
|
||||||
|
@ -147,14 +147,14 @@ print_spec_stats(FILE *out, OpcodeStats *stats)
|
||||||
for (int j = 0; j < SPECIALIZATION_FAILURE_KINDS; j++) {
|
for (int j = 0; j < SPECIALIZATION_FAILURE_KINDS; j++) {
|
||||||
uint64_t val = stats[i].specialization.failure_kinds[j];
|
uint64_t val = stats[i].specialization.failure_kinds[j];
|
||||||
if (val) {
|
if (val) {
|
||||||
fprintf(out, " opcode[%d].specialization.failure_kinds[%d] : %"
|
fprintf(out, " opcode[%s].specialization.failure_kinds[%d] : %"
|
||||||
PRIu64 "\n", i, j, val);
|
PRIu64 "\n", _PyOpcode_OpName[i], j, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int j = 0; j < 256; j++) {
|
for (int j = 0; j < 256; j++) {
|
||||||
if (stats[i].pair_count[j]) {
|
if (stats[i].pair_count[j]) {
|
||||||
fprintf(out, "opcode[%d].pair_count[%d] : %" PRIu64 "\n",
|
fprintf(out, "opcode[%s].pair_count[%s] : %" PRIu64 "\n",
|
||||||
i, j, stats[i].pair_count[j]);
|
_PyOpcode_OpName[i], _PyOpcode_OpName[j], stats[i].pair_count[j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,22 +16,6 @@ if os.name == "nt":
|
||||||
else:
|
else:
|
||||||
DEFAULT_DIR = "/tmp/py_stats/"
|
DEFAULT_DIR = "/tmp/py_stats/"
|
||||||
|
|
||||||
#Create list of all instruction names
|
|
||||||
specialized = iter(opcode._specialized_opmap.keys())
|
|
||||||
opname = ["<0>"]
|
|
||||||
for name in opcode.opname[1:]:
|
|
||||||
if name.startswith("<"):
|
|
||||||
try:
|
|
||||||
name = next(specialized)
|
|
||||||
except StopIteration:
|
|
||||||
pass
|
|
||||||
opname.append(name)
|
|
||||||
|
|
||||||
# opcode_name --> opcode
|
|
||||||
# Sort alphabetically.
|
|
||||||
opmap = {name: i for i, name in enumerate(opname)}
|
|
||||||
opmap = dict(sorted(opmap.items()))
|
|
||||||
|
|
||||||
TOTAL = "specialization.hit", "specialization.miss", "execution_count"
|
TOTAL = "specialization.hit", "specialization.miss", "execution_count"
|
||||||
|
|
||||||
def format_ratio(num, den):
|
def format_ratio(num, den):
|
||||||
|
@ -200,12 +184,12 @@ def gather_stats(input):
|
||||||
raise ValueError(f"{input:r} is not a file or directory path")
|
raise ValueError(f"{input:r} is not a file or directory path")
|
||||||
|
|
||||||
def extract_opcode_stats(stats):
|
def extract_opcode_stats(stats):
|
||||||
opcode_stats = [ {} for _ in range(256) ]
|
opcode_stats = collections.defaultdict(dict)
|
||||||
for key, value in stats.items():
|
for key, value in stats.items():
|
||||||
if not key.startswith("opcode"):
|
if not key.startswith("opcode"):
|
||||||
continue
|
continue
|
||||||
n, _, rest = key[7:].partition("]")
|
name, _, rest = key[7:].partition("]")
|
||||||
opcode_stats[int(n)][rest.strip(".")] = value
|
opcode_stats[name][rest.strip(".")] = value
|
||||||
return opcode_stats
|
return opcode_stats
|
||||||
|
|
||||||
def parse_kinds(spec_src, prefix="SPEC_FAIL"):
|
def parse_kinds(spec_src, prefix="SPEC_FAIL"):
|
||||||
|
@ -246,11 +230,10 @@ def categorized_counts(opcode_stats):
|
||||||
specialized_instructions = {
|
specialized_instructions = {
|
||||||
op for op in opcode._specialized_opmap.keys()
|
op for op in opcode._specialized_opmap.keys()
|
||||||
if "__" not in op}
|
if "__" not in op}
|
||||||
for i, opcode_stat in enumerate(opcode_stats):
|
for name, opcode_stat in opcode_stats.items():
|
||||||
if "execution_count" not in opcode_stat:
|
if "execution_count" not in opcode_stat:
|
||||||
continue
|
continue
|
||||||
count = opcode_stat['execution_count']
|
count = opcode_stat['execution_count']
|
||||||
name = opname[i]
|
|
||||||
if "specializable" in opcode_stat:
|
if "specializable" in opcode_stat:
|
||||||
not_specialized += count
|
not_specialized += count
|
||||||
elif name in specialized_instructions:
|
elif name in specialized_instructions:
|
||||||
|
@ -314,13 +297,13 @@ def emit_table(header, rows):
|
||||||
|
|
||||||
def calculate_execution_counts(opcode_stats, total):
|
def calculate_execution_counts(opcode_stats, total):
|
||||||
counts = []
|
counts = []
|
||||||
for i, opcode_stat in enumerate(opcode_stats):
|
for name, opcode_stat in opcode_stats.items():
|
||||||
if "execution_count" in opcode_stat:
|
if "execution_count" in opcode_stat:
|
||||||
count = opcode_stat['execution_count']
|
count = opcode_stat['execution_count']
|
||||||
miss = 0
|
miss = 0
|
||||||
if "specializable" not in opcode_stat:
|
if "specializable" not in opcode_stat:
|
||||||
miss = opcode_stat.get("specialization.miss")
|
miss = opcode_stat.get("specialization.miss")
|
||||||
counts.append((count, opname[i], miss))
|
counts.append((count, name, miss))
|
||||||
counts.sort(reverse=True)
|
counts.sort(reverse=True)
|
||||||
cumulative = 0
|
cumulative = 0
|
||||||
rows = []
|
rows = []
|
||||||
|
@ -381,16 +364,17 @@ def get_defines():
|
||||||
def emit_specialization_stats(opcode_stats):
|
def emit_specialization_stats(opcode_stats):
|
||||||
defines = get_defines()
|
defines = get_defines()
|
||||||
with Section("Specialization stats", summary="specialization stats by family"):
|
with Section("Specialization stats", summary="specialization stats by family"):
|
||||||
for i, opcode_stat in enumerate(opcode_stats):
|
for name, opcode_stat in opcode_stats.items():
|
||||||
name = opname[i]
|
|
||||||
print_specialization_stats(name, opcode_stat, defines)
|
print_specialization_stats(name, opcode_stat, defines)
|
||||||
|
|
||||||
def emit_comparative_specialization_stats(base_opcode_stats, head_opcode_stats):
|
def emit_comparative_specialization_stats(base_opcode_stats, head_opcode_stats):
|
||||||
defines = get_defines()
|
defines = get_defines()
|
||||||
with Section("Specialization stats", summary="specialization stats by family"):
|
with Section("Specialization stats", summary="specialization stats by family"):
|
||||||
for i, (base_opcode_stat, head_opcode_stat) in enumerate(zip(base_opcode_stats, head_opcode_stats)):
|
opcodes = set(base_opcode_stats.keys()) & set(head_opcode_stats.keys())
|
||||||
name = opname[i]
|
for opcode in opcodes:
|
||||||
print_comparative_specialization_stats(name, base_opcode_stat, head_opcode_stat, defines)
|
print_comparative_specialization_stats(
|
||||||
|
opcode, base_opcode_stats[opcode], head_opcode_stats[opcode], defines
|
||||||
|
)
|
||||||
|
|
||||||
def calculate_specialization_effectiveness(opcode_stats, total):
|
def calculate_specialization_effectiveness(opcode_stats, total):
|
||||||
basic, not_specialized, specialized = categorized_counts(opcode_stats)
|
basic, not_specialized, specialized = categorized_counts(opcode_stats)
|
||||||
|
@ -407,12 +391,12 @@ def emit_specialization_overview(opcode_stats, total):
|
||||||
for title, field in (("Deferred", "specialization.deferred"), ("Misses", "specialization.miss")):
|
for title, field in (("Deferred", "specialization.deferred"), ("Misses", "specialization.miss")):
|
||||||
total = 0
|
total = 0
|
||||||
counts = []
|
counts = []
|
||||||
for i, opcode_stat in enumerate(opcode_stats):
|
for name, opcode_stat in opcode_stats.items():
|
||||||
# Avoid double counting misses
|
# Avoid double counting misses
|
||||||
if title == "Misses" and "specializable" in opcode_stat:
|
if title == "Misses" and "specializable" in opcode_stat:
|
||||||
continue
|
continue
|
||||||
value = opcode_stat.get(field, 0)
|
value = opcode_stat.get(field, 0)
|
||||||
counts.append((value, opname[i]))
|
counts.append((value, name))
|
||||||
total += value
|
total += value
|
||||||
counts.sort(reverse=True)
|
counts.sort(reverse=True)
|
||||||
if total:
|
if total:
|
||||||
|
@ -539,29 +523,27 @@ def emit_comparative_gc_stats(base_stats, head_stats):
|
||||||
|
|
||||||
def get_total(opcode_stats):
|
def get_total(opcode_stats):
|
||||||
total = 0
|
total = 0
|
||||||
for opcode_stat in opcode_stats:
|
for opcode_stat in opcode_stats.values():
|
||||||
if "execution_count" in opcode_stat:
|
if "execution_count" in opcode_stat:
|
||||||
total += opcode_stat['execution_count']
|
total += opcode_stat['execution_count']
|
||||||
return total
|
return total
|
||||||
|
|
||||||
def emit_pair_counts(opcode_stats, total):
|
def emit_pair_counts(opcode_stats, total):
|
||||||
pair_counts = []
|
pair_counts = []
|
||||||
for i, opcode_stat in enumerate(opcode_stats):
|
for name_i, opcode_stat in opcode_stats.items():
|
||||||
if i == 0:
|
|
||||||
continue
|
|
||||||
for key, value in opcode_stat.items():
|
for key, value in opcode_stat.items():
|
||||||
if key.startswith("pair_count"):
|
if key.startswith("pair_count"):
|
||||||
x, _, _ = key[11:].partition("]")
|
name_j, _, _ = key[11:].partition("]")
|
||||||
if value:
|
if value:
|
||||||
pair_counts.append((value, (i, int(x))))
|
pair_counts.append((value, (name_i, name_j)))
|
||||||
with Section("Pair counts", summary="Pair counts for top 100 pairs"):
|
with Section("Pair counts", summary="Pair counts for top 100 pairs"):
|
||||||
pair_counts.sort(reverse=True)
|
pair_counts.sort(reverse=True)
|
||||||
cumulative = 0
|
cumulative = 0
|
||||||
rows = []
|
rows = []
|
||||||
for (count, pair) in itertools.islice(pair_counts, 100):
|
for (count, pair) in itertools.islice(pair_counts, 100):
|
||||||
i, j = pair
|
name_i, name_j = pair
|
||||||
cumulative += count
|
cumulative += count
|
||||||
rows.append((opname[i] + " " + opname[j], count, format_ratio(count, total),
|
rows.append((f"{name_i} {name_j}", count, format_ratio(count, total),
|
||||||
format_ratio(cumulative, total)))
|
format_ratio(cumulative, total)))
|
||||||
emit_table(("Pair", "Count:", "Self:", "Cumulative:"),
|
emit_table(("Pair", "Count:", "Self:", "Cumulative:"),
|
||||||
rows
|
rows
|
||||||
|
@ -577,18 +559,18 @@ def emit_pair_counts(opcode_stats, total):
|
||||||
successors[first][second] = count
|
successors[first][second] = count
|
||||||
total_predecessors[second] += count
|
total_predecessors[second] += count
|
||||||
total_successors[first] += count
|
total_successors[first] += count
|
||||||
for name, i in opmap.items():
|
for name in opcode_stats.keys():
|
||||||
total1 = total_predecessors[i]
|
total1 = total_predecessors[name]
|
||||||
total2 = total_successors[i]
|
total2 = total_successors[name]
|
||||||
if total1 == 0 and total2 == 0:
|
if total1 == 0 and total2 == 0:
|
||||||
continue
|
continue
|
||||||
pred_rows = succ_rows = ()
|
pred_rows = succ_rows = ()
|
||||||
if total1:
|
if total1:
|
||||||
pred_rows = [(opname[pred], count, f"{count/total1:.1%}")
|
pred_rows = [(pred, count, f"{count/total1:.1%}")
|
||||||
for (pred, count) in predecessors[i].most_common(5)]
|
for (pred, count) in predecessors[name].most_common(5)]
|
||||||
if total2:
|
if total2:
|
||||||
succ_rows = [(opname[succ], count, f"{count/total2:.1%}")
|
succ_rows = [(succ, count, f"{count/total2:.1%}")
|
||||||
for (succ, count) in successors[i].most_common(5)]
|
for (succ, count) in successors[name].most_common(5)]
|
||||||
with Section(name, 3, f"Successors and predecessors for {name}"):
|
with Section(name, 3, f"Successors and predecessors for {name}"):
|
||||||
emit_table(("Predecessors", "Count:", "Percentage:"),
|
emit_table(("Predecessors", "Count:", "Percentage:"),
|
||||||
pred_rows
|
pred_rows
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue