Skip to content

Commit 5673945

Browse files
authored
GH-128842: Collect JIT memory stats (GH-128941)
1 parent e6c76b9 commit 5673945

File tree

6 files changed

+117
-8
lines changed

6 files changed

+117
-8
lines changed

Include/cpython/pystats.h

+8
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,14 @@ typedef struct _optimization_stats {
141141
uint64_t remove_globals_builtins_changed;
142142
uint64_t remove_globals_incorrect_keys;
143143
uint64_t error_in_opcode[PYSTATS_MAX_UOP_ID + 1];
144+
// JIT memory stats
145+
uint64_t jit_total_memory_size;
146+
uint64_t jit_code_size;
147+
uint64_t jit_trampoline_size;
148+
uint64_t jit_data_size;
149+
uint64_t jit_padding_size;
150+
uint64_t jit_freed_memory_size;
151+
uint64_t trace_total_memory_hist[_Py_UOP_HIST_SIZE];
144152
} OptimizationStats;
145153

146154
typedef struct _rare_event_stats {

Include/internal/pycore_code.h

+2
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ extern void _Py_Specialize_ContainsOp(_PyStackRef value, _Py_CODEUNIT *instr);
373373
do { if (_Py_stats && PyFunction_Check(callable)) _Py_stats->call_stats.eval_calls[name]++; } while (0)
374374
#define GC_STAT_ADD(gen, name, n) do { if (_Py_stats) _Py_stats->gc_stats[(gen)].name += (n); } while (0)
375375
#define OPT_STAT_INC(name) do { if (_Py_stats) _Py_stats->optimization_stats.name++; } while (0)
376+
#define OPT_STAT_ADD(name, n) do { if (_Py_stats) _Py_stats->optimization_stats.name += (n); } while (0)
376377
#define UOP_STAT_INC(opname, name) do { if (_Py_stats) { assert(opname < 512); _Py_stats->optimization_stats.opcode[opname].name++; } } while (0)
377378
#define UOP_PAIR_INC(uopcode, lastuop) \
378379
do { \
@@ -408,6 +409,7 @@ PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void);
408409
#define EVAL_CALL_STAT_INC_IF_FUNCTION(name, callable) ((void)0)
409410
#define GC_STAT_ADD(gen, name, n) ((void)0)
410411
#define OPT_STAT_INC(name) ((void)0)
412+
#define OPT_STAT_ADD(name, n) ((void)0)
411413
#define UOP_STAT_INC(opname, name) ((void)0)
412414
#define UOP_PAIR_INC(uopcode, lastuop) ((void)0)
413415
#define OPT_UNSUPPORTED_OPCODE(opname) ((void)0)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Collect JIT memory stats using pystats. Patch by Diego Russo.

Python/jit.c

+8
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ jit_free(unsigned char *memory, size_t size)
8787
jit_error("unable to free memory");
8888
return -1;
8989
}
90+
OPT_STAT_ADD(jit_freed_memory_size, size);
9091
return 0;
9192
}
9293

@@ -510,6 +511,13 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz
510511
#ifdef MAP_JIT
511512
pthread_jit_write_protect_np(0);
512513
#endif
514+
// Collect memory stats
515+
OPT_STAT_ADD(jit_total_memory_size, total_size);
516+
OPT_STAT_ADD(jit_code_size, code_size);
517+
OPT_STAT_ADD(jit_trampoline_size, state.trampolines.size);
518+
OPT_STAT_ADD(jit_data_size, data_size);
519+
OPT_STAT_ADD(jit_padding_size, padding);
520+
OPT_HIST(total_size, trace_total_memory_hist);
513521
// Update the offsets of each instruction:
514522
for (size_t i = 0; i < length; i++) {
515523
state.instruction_starts[i] += (uintptr_t)memory;

Python/specialize.c

+8
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,14 @@ print_optimization_stats(FILE *out, OptimizationStats *stats)
309309
);
310310
}
311311
}
312+
fprintf(out, "JIT total memory size: %" PRIu64 "\n", stats->jit_total_memory_size);
313+
fprintf(out, "JIT code size: %" PRIu64 "\n", stats->jit_code_size);
314+
fprintf(out, "JIT trampoline size: %" PRIu64 "\n", stats->jit_trampoline_size);
315+
fprintf(out, "JIT data size: %" PRIu64 "\n", stats->jit_data_size);
316+
fprintf(out, "JIT padding size: %" PRIu64 "\n", stats->jit_padding_size);
317+
fprintf(out, "JIT freed memory size: %" PRIu64 "\n", stats->jit_freed_memory_size);
318+
319+
print_histogram(out, "Trace total memory size", stats->trace_total_memory_hist);
312320
}
313321
#endif
314322

Tools/scripts/summarize_stats.py

+90-8
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,41 @@ def get_optimizer_stats(self) -> dict[str, tuple[int, int | None]]:
545545
): (incorrect_keys, attempts),
546546
}
547547

548+
def get_jit_memory_stats(self) -> dict[Doc, tuple[int, int | None]]:
549+
jit_total_memory_size = self._data["JIT total memory size"]
550+
jit_code_size = self._data["JIT code size"]
551+
jit_trampoline_size = self._data["JIT trampoline size"]
552+
jit_data_size = self._data["JIT data size"]
553+
jit_padding_size = self._data["JIT padding size"]
554+
jit_freed_memory_size = self._data["JIT freed memory size"]
555+
556+
return {
557+
Doc(
558+
"Total memory size",
559+
"The total size of the memory allocated for the JIT traces",
560+
): (jit_total_memory_size, None),
561+
Doc(
562+
"Code size",
563+
"The size of the memory allocated for the code of the JIT traces",
564+
): (jit_code_size, jit_total_memory_size),
565+
Doc(
566+
"Trampoline size",
567+
"The size of the memory allocated for the trampolines of the JIT traces",
568+
): (jit_trampoline_size, jit_total_memory_size),
569+
Doc(
570+
"Data size",
571+
"The size of the memory allocated for the data of the JIT traces",
572+
): (jit_data_size, jit_total_memory_size),
573+
Doc(
574+
"Padding size",
575+
"The size of the memory allocated for the padding of the JIT traces",
576+
): (jit_padding_size, jit_total_memory_size),
577+
Doc(
578+
"Freed memory size",
579+
"The size of the memory freed from the JIT traces",
580+
): (jit_freed_memory_size, jit_total_memory_size),
581+
}
582+
548583
def get_histogram(self, prefix: str) -> list[tuple[int, int]]:
549584
rows = []
550585
for k, v in self._data.items():
@@ -1161,26 +1196,51 @@ def calc_optimizer_table(stats: Stats) -> Rows:
11611196
for label, (value, den) in optimizer_stats.items()
11621197
]
11631198

1164-
def calc_histogram_table(key: str, den: str) -> RowCalculator:
1199+
def calc_jit_memory_table(stats: Stats) -> Rows:
1200+
jit_memory_stats = stats.get_jit_memory_stats()
1201+
1202+
return [
1203+
(
1204+
label,
1205+
Count(value),
1206+
Ratio(value, den, percentage=label != "Total memory size"),
1207+
)
1208+
for label, (value, den) in jit_memory_stats.items()
1209+
]
1210+
1211+
def calc_histogram_table(key: str, den: str | None = None) -> RowCalculator:
11651212
def calc(stats: Stats) -> Rows:
11661213
histogram = stats.get_histogram(key)
1167-
denominator = stats.get(den)
1214+
1215+
if den:
1216+
denominator = stats.get(den)
1217+
else:
1218+
denominator = 0
1219+
for _, v in histogram:
1220+
denominator += v
11681221

11691222
rows: Rows = []
1170-
last_non_zero = 0
11711223
for k, v in histogram:
1172-
if v != 0:
1173-
last_non_zero = len(rows)
11741224
rows.append(
11751225
(
11761226
f"<= {k:,d}",
11771227
Count(v),
11781228
Ratio(v, denominator),
11791229
)
11801230
)
1181-
# Don't include any zero entries at the end
1182-
rows = rows[: last_non_zero + 1]
1183-
return rows
1231+
# Don't include any leading and trailing zero entries
1232+
start = 0
1233+
end = len(rows) - 1
1234+
1235+
while start <= end:
1236+
if rows[start][1] == 0:
1237+
start += 1
1238+
elif rows[end][1] == 0:
1239+
end -= 1
1240+
else:
1241+
break
1242+
1243+
return rows[start:end+1]
11841244

11851245
return calc
11861246

@@ -1214,6 +1274,28 @@ def iter_optimization_tables(base_stats: Stats, head_stats: Stats | None = None)
12141274

12151275
yield Table(("", "Count:", "Ratio:"), calc_optimization_table, JoinMode.CHANGE)
12161276
yield Table(("", "Count:", "Ratio:"), calc_optimizer_table, JoinMode.CHANGE)
1277+
yield Section(
1278+
"JIT memory stats",
1279+
"JIT memory stats",
1280+
[
1281+
Table(
1282+
("", "Size (bytes):", "Ratio:"),
1283+
calc_jit_memory_table,
1284+
JoinMode.CHANGE
1285+
)
1286+
],
1287+
)
1288+
yield Section(
1289+
"JIT trace total memory histogram",
1290+
"JIT trace total memory histogram",
1291+
[
1292+
Table(
1293+
("Size (bytes)", "Count", "Ratio:"),
1294+
calc_histogram_table("Trace total memory size"),
1295+
JoinMode.CHANGE_NO_SORT,
1296+
)
1297+
],
1298+
)
12171299
for name, den in [
12181300
("Trace length", "Optimization traces created"),
12191301
("Optimized trace length", "Optimization traces created"),

0 commit comments

Comments
 (0)