From 2dfa47cd46b4b40fde4bb01ac5b7d1bf0c0f7afb Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 16 Apr 2025 13:47:12 +0100 Subject: [PATCH 1/3] Add stats for GET_ITER --- Include/internal/pycore_code.h | 1 + Python/bytecodes.c | 3 +++ Python/executor_cases.c.h | 5 +++++ Python/generated_cases.c.h | 5 +++++ Python/specialize.c | 12 ++++++++++++ 5 files changed, 26 insertions(+) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 2839b9b7ebe0fb..7457d8910f5604 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -315,6 +315,7 @@ extern void _Py_Specialize_ForIter(_PyStackRef iter, _Py_CODEUNIT *instr, int op extern void _Py_Specialize_Send(_PyStackRef receiver, _Py_CODEUNIT *instr); extern void _Py_Specialize_ToBool(_PyStackRef value, _Py_CODEUNIT *instr); extern void _Py_Specialize_ContainsOp(_PyStackRef value, _Py_CODEUNIT *instr); +extern void _Py_GatherStats_GetIter(_PyStackRef iterable); // Utility functions for reading/writing 32/64-bit values in the inline caches. // Great care should be taken to ensure that these functions remain correct and diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 95786c91371e98..1b1cc007480c12 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -3026,6 +3026,9 @@ dummy_func( } inst(GET_ITER, (iterable -- iter)) { + #ifdef Py_STATS + _Py_GatherStats_GetIter(iterable); + #endif /* before: [obj]; after [getiter(obj)] */ PyObject *iter_o = PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable)); PyStackRef_CLOSE(iterable); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 9bfb13e2d9773f..48b28aeb223510 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -4076,6 +4076,11 @@ _PyStackRef iterable; _PyStackRef iter; iterable = stack_pointer[-1]; + #ifdef Py_STATS + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_GatherStats_GetIter(iterable); + stack_pointer = _PyFrame_GetStackPointer(frame); + #endif _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *iter_o = PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable)); stack_pointer = _PyFrame_GetStackPointer(frame); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 6fe647d6197a07..6bb87edf8b3673 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -5920,6 +5920,11 @@ _PyStackRef iterable; _PyStackRef iter; iterable = stack_pointer[-1]; + #ifdef Py_STATS + _PyFrame_SetStackPointer(frame, stack_pointer); + _Py_GatherStats_GetIter(iterable); + stack_pointer = _PyFrame_GetStackPointer(frame); + #endif _PyFrame_SetStackPointer(frame, stack_pointer); PyObject *iter_o = PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable)); stack_pointer = _PyFrame_GetStackPointer(frame); diff --git a/Python/specialize.c b/Python/specialize.c index ceb396c5b54815..2e8be62d81f8f4 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -146,6 +146,7 @@ print_spec_stats(FILE *out, OpcodeStats *stats) * even though we don't specialize them yet. */ fprintf(out, "opcode[BINARY_SLICE].specializable : 1\n"); fprintf(out, "opcode[STORE_SLICE].specializable : 1\n"); + fprintf(out, "opcode[GET_ITER].specializable : 1\n"); for (int i = 0; i < 256; i++) { if (_PyOpcode_Caches[i]) { /* Ignore jumps as they cannot be specialized */ @@ -3114,6 +3115,17 @@ _Py_Specialize_ContainsOp(_PyStackRef value_st, _Py_CODEUNIT *instr) return; } +#ifdef Py_STATS +void +_Py_GatherStats_GetIter(_PyStackRef iterable) +{ + PyObject *obj = PyStackRef_AsPyObjectBorrow(iterable); + int kind = _PySpecialization_ClassifyIterator(obj); + SPECIALIZATION_FAIL(GET_ITER, kind); +} +#endif + + /* Code init cleanup. * CALL_ALLOC_AND_ENTER_INIT will set up * the frame to execute the EXIT_INIT_CHECK From 10cd875257bf39f99982b053d92302d45d7ec0c7 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 16 Apr 2025 14:14:13 +0100 Subject: [PATCH 2/3] Look for common iterable types, not iterator types --- Python/specialize.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/Python/specialize.c b/Python/specialize.c index 2e8be62d81f8f4..6f490b47ff1892 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -3119,8 +3119,41 @@ _Py_Specialize_ContainsOp(_PyStackRef value_st, _Py_CODEUNIT *instr) void _Py_GatherStats_GetIter(_PyStackRef iterable) { - PyObject *obj = PyStackRef_AsPyObjectBorrow(iterable); - int kind = _PySpecialization_ClassifyIterator(obj); + PyTypeObject *tp = PyStackRef_TYPE(iterable); + int kind = SPEC_FAIL_OTHER; + if (tp == &PyTuple_Type) { + kind = SPEC_FAIL_ITER_TUPLE; + } + else if (tp == &PyList_Type) { + kind = SPEC_FAIL_ITER_LIST; + } + else if (tp == &PyDict_Type) { + kind = SPEC_FAIL_ITER_DICT_KEYS; + } + else if (tp == &PySet_Type) { + kind = SPEC_FAIL_ITER_SET; + } + else if (tp == &PyBytes_Type) { + kind = SPEC_FAIL_ITER_BYTES; + } + else if (tp == &PyEnum_Type) { + kind = SPEC_FAIL_ITER_ENUMERATE; + } + else if (tp == &PyUnicode_Type) { + kind = SPEC_FAIL_ITER_STRING; + } + else if (tp == &PyGen_Type) { + kind = SPEC_FAIL_ITER_GENERATOR; + } + else if (tp == &PyCoro_Type) { + kind = SPEC_FAIL_ITER_COROUTINE; + } + else if (tp == &PyAsyncGen_Type) { + kind = SPEC_FAIL_ITER_ASYNC_GENERATOR; + } + else if (tp == &_PyAsyncGenASend_Type) { + kind = SPEC_FAIL_ITER_ASYNC_GENERATOR_SEND; + } SPECIALIZATION_FAIL(GET_ITER, kind); } #endif From a45557363d328d1e55c0a0add1276698b2a88c13 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 28 Apr 2025 18:14:38 +0100 Subject: [PATCH 3/3] Add stats for self iter and fix naming in summary --- Python/specialize.c | 4 ++++ Tools/scripts/summarize_stats.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Python/specialize.c b/Python/specialize.c index 6f490b47ff1892..618b8249ebbd71 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -669,6 +669,7 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, int enable_counters #define SPEC_FAIL_ITER_CALLABLE 28 #define SPEC_FAIL_ITER_ASCII_STRING 29 #define SPEC_FAIL_ITER_ASYNC_GENERATOR_SEND 30 +#define SPEC_FAIL_ITER_SELF 31 // UNPACK_SEQUENCE @@ -3154,6 +3155,9 @@ _Py_GatherStats_GetIter(_PyStackRef iterable) else if (tp == &_PyAsyncGenASend_Type) { kind = SPEC_FAIL_ITER_ASYNC_GENERATOR_SEND; } + else if (tp->tp_iter == PyObject_SelfIter) { + kind = SPEC_FAIL_ITER_SELF; + } SPECIALIZATION_FAIL(GET_ITER, kind); } #endif diff --git a/Tools/scripts/summarize_stats.py b/Tools/scripts/summarize_stats.py index eb54b8dd115c94..68cfad3f92cdc7 100644 --- a/Tools/scripts/summarize_stats.py +++ b/Tools/scripts/summarize_stats.py @@ -288,7 +288,7 @@ def kind_to_text(kind: int, opcode: str): opcode = "SUPER" elif opcode.endswith("ATTR"): opcode = "ATTR" - elif opcode in ("FOR_ITER", "SEND"): + elif opcode in ("FOR_ITER", "GET_ITER", "SEND"): opcode = "ITER" elif opcode.endswith("SUBSCR"): opcode = "SUBSCR"