Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e659d4c

Browse files
committedJan 23, 2025
gh-128842: collect JIT memory stats via pystats
Collect via pystats the following metrics: * total memory size * code size * trampoline size * data size * padding seze * freed memory size
1 parent 298dda5 commit e659d4c

File tree

5 files changed

+80
-0
lines changed

5 files changed

+80
-0
lines changed
 

‎Include/cpython/pystats.h

+7
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,13 @@ 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;
144151
} OptimizationStats;
145152

146153
typedef struct _rare_event_stats {

‎Include/internal/pycore_code.h

+2
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ extern void _Py_Specialize_ContainsOp(_PyStackRef value, _Py_CODEUNIT *instr);
375375
do { if (_Py_stats && PyFunction_Check(callable)) _Py_stats->call_stats.eval_calls[name]++; } while (0)
376376
#define GC_STAT_ADD(gen, name, n) do { if (_Py_stats) _Py_stats->gc_stats[(gen)].name += (n); } while (0)
377377
#define OPT_STAT_INC(name) do { if (_Py_stats) _Py_stats->optimization_stats.name++; } while (0)
378+
#define OPT_STAT_ADD(name, n) do { if (_Py_stats) _Py_stats->optimization_stats.name += (n); } while (0)
378379
#define UOP_STAT_INC(opname, name) do { if (_Py_stats) { assert(opname < 512); _Py_stats->optimization_stats.opcode[opname].name++; } } while (0)
379380
#define UOP_PAIR_INC(uopcode, lastuop) \
380381
do { \
@@ -410,6 +411,7 @@ PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void);
410411
#define EVAL_CALL_STAT_INC_IF_FUNCTION(name, callable) ((void)0)
411412
#define GC_STAT_ADD(gen, name, n) ((void)0)
412413
#define OPT_STAT_INC(name) ((void)0)
414+
#define OPT_STAT_ADD(name, n) ((void)0)
413415
#define UOP_STAT_INC(opname, name) ((void)0)
414416
#define UOP_PAIR_INC(uopcode, lastuop) ((void)0)
415417
#define OPT_UNSUPPORTED_OPCODE(opname) ((void)0)

‎Python/jit.c

+7
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,12 @@ _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);
513520
// Update the offsets of each instruction:
514521
for (size_t i = 0; i < length; i++) {
515522
state.instruction_starts[i] += (uintptr_t)memory;

‎Python/specialize.c

+6
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,12 @@ 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);
312318
}
313319
#endif
314320

‎Tools/scripts/summarize_stats.py

+58
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,6 +1196,18 @@ def calc_optimizer_table(stats: Stats) -> Rows:
11611196
for label, (value, den) in optimizer_stats.items()
11621197
]
11631198

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+
11641211
def calc_histogram_table(key: str, den: str) -> RowCalculator:
11651212
def calc(stats: Stats) -> Rows:
11661213
histogram = stats.get_histogram(key)
@@ -1214,6 +1261,17 @@ def iter_optimization_tables(base_stats: Stats, head_stats: Stats | None = None)
12141261

12151262
yield Table(("", "Count:", "Ratio:"), calc_optimization_table, JoinMode.CHANGE)
12161263
yield Table(("", "Count:", "Ratio:"), calc_optimizer_table, JoinMode.CHANGE)
1264+
yield Section(
1265+
"JIT memory stats",
1266+
"",
1267+
[
1268+
Table(
1269+
("", "Size (bytes):", "Ratio:"),
1270+
calc_jit_memory_table,
1271+
JoinMode.CHANGE
1272+
)
1273+
],
1274+
)
12171275
for name, den in [
12181276
("Trace length", "Optimization traces created"),
12191277
("Optimized trace length", "Optimization traces created"),

0 commit comments

Comments
 (0)
Please sign in to comment.