-
- Notifications
You must be signed in to change notification settings - Fork 33.4k
gh-109329: Support for basic pystats for Tier 2 #109913
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
7ba5f55 b84f496 5549410 ed639fc 195664e 8d76c0a c54707a ba58546 08cd8c0 8d2699d ad0ca42 e5ee4e5 0dac9e5 69a6e7b d7c1403 aa69c0f 5c3599a File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| | @@ -282,6 +282,10 @@ extern int _PyStaticCode_Init(PyCodeObject *co); | |
| #define EVAL_CALL_STAT_INC_IF_FUNCTION(name, callable) \ | ||
| do { if (_Py_stats && PyFunction_Check(callable)) _Py_stats->call_stats.eval_calls[name]++; } while (0) | ||
| #define GC_STAT_ADD(gen, name, n) do { if (_Py_stats) _Py_stats->gc_stats[(gen)].name += (n); } while (0) | ||
| #define OPT_STAT_INC(name) do { if (_Py_stats) _Py_stats->optimization_stats.name++; } while (0) | ||
| #define UOP_EXE_INC(opname) do { if (_Py_stats) _Py_stats->optimization_stats.opcode[opname].execution_count++; } while (0) | ||
| #define OPT_UNSUPPORTED_OPCODE(opname) do { if (_Py_stats) _Py_stats->optimization_stats.unsupported_opcode[opname]++; } while (0) | ||
| #define OPT_HIST(length, name) do { if (_Py_stats) _Py_stats->optimization_stats.name[_Py_bit_length((length - 1) >> 2)]++; } while (0) | ||
| ||
| | ||
| // Export for '_opcode' shared extension | ||
| PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void); | ||
| | @@ -296,6 +300,10 @@ PyAPI_FUNC(PyObject*) _Py_GetSpecializationStats(void); | |
| #define EVAL_CALL_STAT_INC(name) ((void)0) | ||
| #define EVAL_CALL_STAT_INC_IF_FUNCTION(name, callable) ((void)0) | ||
| #define GC_STAT_ADD(gen, name, n) ((void)0) | ||
| #define OPT_STAT_INC(name) ((void)0) | ||
| #define UOP_EXE_INC(opname) ((void)0) | ||
| #define OPT_UNSUPPORTED_OPCODE(opname) ((void)0) | ||
| #define OPT_HIST(length, name) ((void)0) | ||
| #endif // !Py_STATS | ||
| | ||
| // Utility functions for reading/writing 32/64-bit values in the inline caches. | ||
| | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| | @@ -199,10 +199,6 @@ print_object_stats(FILE *out, ObjectStats *stats) | |
| fprintf(out, "Object method cache collisions: %" PRIu64 "\n", stats->type_cache_collisions); | ||
| fprintf(out, "Object method cache dunder hits: %" PRIu64 "\n", stats->type_cache_dunder_hits); | ||
| fprintf(out, "Object method cache dunder misses: %" PRIu64 "\n", stats->type_cache_dunder_misses); | ||
| fprintf(out, "Optimization attempts: %" PRIu64 "\n", stats->optimization_attempts); | ||
| fprintf(out, "Optimization traces created: %" PRIu64 "\n", stats->optimization_traces_created); | ||
| fprintf(out, "Optimization traces executed: %" PRIu64 "\n", stats->optimization_traces_executed); | ||
| fprintf(out, "Optimization uops executed: %" PRIu64 "\n", stats->optimization_uops_executed); | ||
| } | ||
| | ||
| static void | ||
| | @@ -215,13 +211,63 @@ print_gc_stats(FILE *out, GCStats *stats) | |
| } | ||
| } | ||
| | ||
| static void | ||
| print_histogram(FILE *out, const char *name, uint64_t hist[_Py_UOP_HIST_SIZE]) | ||
| { | ||
| for (int i = 0; i < _Py_UOP_HIST_SIZE; i++) { | ||
| fprintf(out, "%s[%d]: %" PRIu64 "\n", name, (1 << (i + 2)), hist[i]); | ||
| ||
| } | ||
| } | ||
| | ||
| static void | ||
| print_optimization_stats(FILE *out, OptimizationStats *stats) | ||
| { | ||
| fprintf(out, "Optimization attempts: %" PRIu64 "\n", stats->attempts); | ||
| fprintf(out, "Optimization traces created: %" PRIu64 "\n", stats->traces_created); | ||
| fprintf(out, "Optimization traces executed: %" PRIu64 "\n", stats->traces_executed); | ||
| fprintf(out, "Optimization uops executed: %" PRIu64 "\n", stats->uops_executed); | ||
| fprintf(out, "Optimization trace stack overflow: %" PRIu64 "\n", stats->trace_stack_overflow); | ||
| fprintf(out, "Optimization trace stack underflow: %" PRIu64 "\n", stats->trace_stack_underflow); | ||
| fprintf(out, "Optimization trace too long: %" PRIu64 "\n", stats->trace_too_long); | ||
| fprintf(out, "Optimization inner loop: %" PRIu64 "\n", stats->inner_loop); | ||
| fprintf(out, "Optimization recursive call: %" PRIu64 "\n", stats->recursive_call); | ||
| | ||
| print_histogram(out, "Trace length", stats->trace_length_hist); | ||
| print_histogram(out, "Trace run length", stats->trace_run_length_hist); | ||
| print_histogram(out, "Optimized trace length", stats->optimized_trace_length_hist); | ||
| | ||
| const char* const* names; | ||
| for (int i = 0; i < 512; i++) { | ||
| if (i < 256) { | ||
| names = _PyOpcode_OpName; | ||
| } else { | ||
| names = _PyOpcode_uop_name; | ||
| } | ||
| if (stats->opcode[i].execution_count) { | ||
| fprintf(out, "uops[%s].execution_count : %" PRIu64 "\n", names[i], stats->opcode[i].execution_count); | ||
| } | ||
| } | ||
| | ||
| for (int i = 0; i < 256; i++) { | ||
| if (stats->unsupported_opcode[i]) { | ||
| fprintf( | ||
| out, | ||
| "unsupported_opcode[%s].count : %" PRIu64 "\n", | ||
| _PyOpcode_OpName[i], | ||
| stats->unsupported_opcode[i] | ||
| ); | ||
| } | ||
| } | ||
| } | ||
| | ||
| static void | ||
| print_stats(FILE *out, PyStats *stats) | ||
| { | ||
| print_spec_stats(out, stats->opcode_stats); | ||
| print_call_stats(out, &stats->call_stats); | ||
| print_object_stats(out, &stats->object_stats); | ||
| print_gc_stats(out, stats->gc_stats); | ||
| print_optimization_stats(out, &stats->optimization_stats); | ||
| } | ||
| | ||
| void | ||
| | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll probably want more buckets than this (since execution lengths for closed loops will get pretty big), but it's probably fine for now. We can just bump it whenever.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the max trace length was 64 (
_Py_UOP_MAX_TRACE_LENGTH). Can they be longer? (It's quite possible I don't understand the code). In any event, I should probably add a comment here that this should be log2 of whatever the expected maximum length is.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not statically, but dynamically they can be quite long (imagine executing a trace for a closed loop hundreds of times).
So we should probably modify the code that adds to the histogram to be
Py_MAX(_Py_UOP_HIST_SIZE - 1, _Py_bit_length((length - 1) >> _Py_UOP_HIST_BIAS)), so that anything overflowing the histogram ends up in the largest bucket instead of writing past the end of the array.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's a good idea to add the range-checking.
Also, this made me realize I was counting the wrong thing -- I was recording the "instruction pointer at exit" to be the execution length. But (after discussing in person) it seems what we want is the number of uops executed per execution of the trace. I've updated this to report that instead.