Skip to content

bpo-46072: Add detailed failure stats for BINARY_OP #31289

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

Merged
merged 5 commits into from
Feb 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add more detailed specialization failure statistics for :opcode:`BINARY_OP`.
115 changes: 103 additions & 12 deletions Python/specialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -543,9 +543,28 @@ initial_counter_value(void) {
#define SPEC_FAIL_SUBSCR_PY_OTHER 21
#define SPEC_FAIL_SUBSCR_DICT_SUBCLASS_NO_OVERRIDE 22

/* Binary add */

#define SPEC_FAIL_BINARY_OP_DIFFERENT_TYPES 12
/* Binary op */

#define SPEC_FAIL_BINARY_OP_ADD_DIFFERENT_TYPES 8
#define SPEC_FAIL_BINARY_OP_ADD_OTHER 9
#define SPEC_FAIL_BINARY_OP_AND_DIFFERENT_TYPES 10
#define SPEC_FAIL_BINARY_OP_AND_INT 11
#define SPEC_FAIL_BINARY_OP_AND_OTHER 12
#define SPEC_FAIL_BINARY_OP_FLOOR_DIVIDE 13
#define SPEC_FAIL_BINARY_OP_LSHIFT 14
#define SPEC_FAIL_BINARY_OP_MATRIX_MULTIPLY 15
#define SPEC_FAIL_BINARY_OP_MULTIPLY_DIFFERENT_TYPES 16
#define SPEC_FAIL_BINARY_OP_MULTIPLY_OTHER 17
#define SPEC_FAIL_BINARY_OP_OR 18
#define SPEC_FAIL_BINARY_OP_POWER 19
#define SPEC_FAIL_BINARY_OP_REMAINDER 20
#define SPEC_FAIL_BINARY_OP_RSHIFT 21
#define SPEC_FAIL_BINARY_OP_SUBTRACT_DIFFERENT_TYPES 22
#define SPEC_FAIL_BINARY_OP_SUBTRACT_OTHER 23
#define SPEC_FAIL_BINARY_OP_TRUE_DIVIDE_DIFFERENT_TYPES 24
#define SPEC_FAIL_BINARY_OP_TRUE_DIVIDE_FLOAT 25
#define SPEC_FAIL_BINARY_OP_TRUE_DIVIDE_OTHER 26
#define SPEC_FAIL_BINARY_OP_XOR 27

/* Calls */
#define SPEC_FAIL_CALL_COMPLEX_PARAMETERS 9
Expand Down Expand Up @@ -1735,6 +1754,76 @@ _Py_Specialize_CallNoKw(
return 0;
}

#ifdef Py_STATS
static int
binary_op_fail_kind(int oparg, PyObject *lhs, PyObject *rhs)
{
switch (oparg) {
case NB_ADD:
case NB_INPLACE_ADD:
if (!Py_IS_TYPE(lhs, Py_TYPE(rhs))) {
return SPEC_FAIL_BINARY_OP_ADD_DIFFERENT_TYPES;
}
return SPEC_FAIL_BINARY_OP_ADD_OTHER;
case NB_AND:
case NB_INPLACE_AND:
if (!Py_IS_TYPE(lhs, Py_TYPE(rhs))) {
return SPEC_FAIL_BINARY_OP_AND_DIFFERENT_TYPES;
}
if (PyLong_CheckExact(lhs)) {
return SPEC_FAIL_BINARY_OP_AND_INT;
}
return SPEC_FAIL_BINARY_OP_AND_OTHER;
case NB_FLOOR_DIVIDE:
case NB_INPLACE_FLOOR_DIVIDE:
return SPEC_FAIL_BINARY_OP_FLOOR_DIVIDE;
case NB_LSHIFT:
case NB_INPLACE_LSHIFT:
return SPEC_FAIL_BINARY_OP_LSHIFT;
case NB_MATRIX_MULTIPLY:
case NB_INPLACE_MATRIX_MULTIPLY:
return SPEC_FAIL_BINARY_OP_MATRIX_MULTIPLY;
case NB_MULTIPLY:
case NB_INPLACE_MULTIPLY:
if (!Py_IS_TYPE(lhs, Py_TYPE(rhs))) {
return SPEC_FAIL_BINARY_OP_MULTIPLY_DIFFERENT_TYPES;
}
return SPEC_FAIL_BINARY_OP_MULTIPLY_OTHER;
case NB_OR:
case NB_INPLACE_OR:
return SPEC_FAIL_BINARY_OP_OR;
case NB_POWER:
case NB_INPLACE_POWER:
return SPEC_FAIL_BINARY_OP_POWER;
case NB_REMAINDER:
case NB_INPLACE_REMAINDER:
return SPEC_FAIL_BINARY_OP_REMAINDER;
case NB_RSHIFT:
case NB_INPLACE_RSHIFT:
return SPEC_FAIL_BINARY_OP_RSHIFT;
case NB_SUBTRACT:
case NB_INPLACE_SUBTRACT:
if (!Py_IS_TYPE(lhs, Py_TYPE(rhs))) {
return SPEC_FAIL_BINARY_OP_SUBTRACT_DIFFERENT_TYPES;
}
return SPEC_FAIL_BINARY_OP_SUBTRACT_OTHER;
case NB_TRUE_DIVIDE:
case NB_INPLACE_TRUE_DIVIDE:
if (!Py_IS_TYPE(lhs, Py_TYPE(rhs))) {
return SPEC_FAIL_BINARY_OP_TRUE_DIVIDE_DIFFERENT_TYPES;
}
if (PyFloat_CheckExact(lhs)) {
return SPEC_FAIL_BINARY_OP_TRUE_DIVIDE_FLOAT;
}
return SPEC_FAIL_BINARY_OP_TRUE_DIVIDE_OTHER;
case NB_XOR:
case NB_INPLACE_XOR:
return SPEC_FAIL_BINARY_OP_XOR;
}
Py_UNREACHABLE();
}
#endif

void
_Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
SpecializedCacheEntry *cache)
Expand All @@ -1744,8 +1833,7 @@ _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
case NB_ADD:
case NB_INPLACE_ADD:
if (!Py_IS_TYPE(lhs, Py_TYPE(rhs))) {
SPECIALIZATION_FAIL(BINARY_OP, SPEC_FAIL_BINARY_OP_DIFFERENT_TYPES);
goto failure;
break;
}
if (PyUnicode_CheckExact(lhs)) {
if (_Py_OPCODE(instr[1]) == STORE_FAST && Py_REFCNT(lhs) == 2) {
Expand All @@ -1770,8 +1858,7 @@ _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
case NB_MULTIPLY:
case NB_INPLACE_MULTIPLY:
if (!Py_IS_TYPE(lhs, Py_TYPE(rhs))) {
SPECIALIZATION_FAIL(BINARY_OP, SPEC_FAIL_BINARY_OP_DIFFERENT_TYPES);
goto failure;
break;
}
if (PyLong_CheckExact(lhs)) {
*instr = _Py_MAKECODEUNIT(BINARY_OP_MULTIPLY_INT,
Expand All @@ -1787,8 +1874,7 @@ _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
case NB_SUBTRACT:
case NB_INPLACE_SUBTRACT:
if (!Py_IS_TYPE(lhs, Py_TYPE(rhs))) {
SPECIALIZATION_FAIL(BINARY_OP, SPEC_FAIL_BINARY_OP_DIFFERENT_TYPES);
goto failure;
break;
}
if (PyLong_CheckExact(lhs)) {
*instr = _Py_MAKECODEUNIT(BINARY_OP_SUBTRACT_INT,
Expand All @@ -1801,14 +1887,19 @@ _Py_Specialize_BinaryOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
goto success;
}
break;
#ifndef Py_STATS
default:
// These operators don't have any available specializations. Rather
// than repeatedly attempting to specialize them, just convert them
// back to BINARY_OP (while still recording a failure, of course)!
// back to BINARY_OP (unless we're collecting stats, where it's more
// important to get accurate hit counts for the unadaptive version
// and each of the different failure types):
*instr = _Py_MAKECODEUNIT(BINARY_OP, adaptive->original_oparg);
return;
#endif
}
SPECIALIZATION_FAIL(BINARY_OP, SPEC_FAIL_OTHER);
failure:
SPECIALIZATION_FAIL(
BINARY_OP, binary_op_fail_kind(adaptive->original_oparg, lhs, rhs));
STAT_INC(BINARY_OP, failure);
cache_backoff(adaptive);
return;
Expand Down