| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -227,9 +227,6 @@ extern void _PyLineTable_InitAddressRange( | |||
| 227 | 227 | extern int _PyLineTable_NextAddressRange(PyCodeAddressRange *range); | |
| 228 | 228 | extern int _PyLineTable_PreviousAddressRange(PyCodeAddressRange *range); | |
| 229 | 229 | ||
| 230 | - | ||
| 231 | - #define ADAPTIVE_CACHE_BACKOFF 64 | ||
| 232 | - | ||
| 233 | 230 | /* Specialization functions */ | |
| 234 | 231 | ||
| 235 | 232 | extern int _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, | |
@@ -423,6 +420,50 @@ write_location_entry_start(uint8_t *ptr, int code, int length) | |||
| 423 | 420 | } | |
| 424 | 421 | ||
| 425 | 422 | ||
| 423 | + /** Counters | ||
| 424 | + * The first 16-bit value in each inline cache is a counter. | ||
| 425 | + * When counting misses, the counter is treated as a simple unsigned value. | ||
| 426 | + * | ||
| 427 | + * When counting executions until the next specialization attempt, | ||
| 428 | + * exponential backoff is used to reduce the number of specialization failures. | ||
| 429 | + * The high 12 bits store the counter, the low 4 bits store the backoff exponent. | ||
| 430 | + * On a specialization failure, the backoff exponent is incremented and the | ||
| 431 | + * counter set to (2**backoff - 1). | ||
| 432 | + * Backoff == 6 -> starting counter == 63, backoff == 10 -> starting counter == 1023. | ||
| 433 | + */ | ||
| 434 | + | ||
| 435 | + /* With a 16-bit counter, we have 12 bits for the counter value, and 4 bits for the backoff */ | ||
| 436 | + #define ADAPTIVE_BACKOFF_BITS 4 | ||
| 437 | + /* The initial counter value is 31 == 2**ADAPTIVE_BACKOFF_START - 1 */ | ||
| 438 | + #define ADAPTIVE_BACKOFF_START 5 | ||
| 439 | + | ||
| 440 | + #define MAX_BACKOFF_VALUE (16 - ADAPTIVE_BACKOFF_BITS) | ||
| 441 | + | ||
| 442 | + | ||
| 443 | + static inline uint16_t | ||
| 444 | + adaptive_counter_bits(int value, int backoff) { | ||
| 445 | + return (value << ADAPTIVE_BACKOFF_BITS) | | ||
| 446 | + (backoff & ((1<<ADAPTIVE_BACKOFF_BITS)-1)); | ||
| 447 | + } | ||
| 448 | + | ||
| 449 | + static inline uint16_t | ||
| 450 | + adaptive_counter_start(void) { | ||
| 451 | + unsigned int value = (1 << ADAPTIVE_BACKOFF_START) - 1; | ||
| 452 | + return adaptive_counter_bits(value, ADAPTIVE_BACKOFF_START); | ||
| 453 | + } | ||
| 454 | + | ||
| 455 | + static inline uint16_t | ||
| 456 | + adaptive_counter_backoff(uint16_t counter) { | ||
| 457 | + unsigned int backoff = counter & ((1<<ADAPTIVE_BACKOFF_BITS)-1); | ||
| 458 | + backoff++; | ||
| 459 | + if (backoff > MAX_BACKOFF_VALUE) { | ||
| 460 | + backoff = MAX_BACKOFF_VALUE; | ||
| 461 | + } | ||
| 462 | + unsigned int value = (1 << backoff) - 1; | ||
| 463 | + return adaptive_counter_bits(value, backoff); | ||
| 464 | + } | ||
| 465 | + | ||
| 466 | + | ||
| 426 | 467 | #ifdef __cplusplus | |
| 427 | 468 | } | |
| 428 | 469 | #endif | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,3 @@ | |||
| 1 | + Use exponential backoff for specialization counters in the interpreter. Can | ||
| 2 | + reduce the number of failed specializations significantly and avoid slowdown | ||
| 3 | + for those parts of a program that are not suitable for specialization. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1561,7 +1561,11 @@ eval_frame_handle_pending(PyThreadState *tstate) | |||
| 1561 | 1561 | dtrace_function_entry(frame); \ | |
| 1562 | 1562 | } | |
| 1563 | 1563 | ||
| 1564 | + #define ADAPTIVE_COUNTER_IS_ZERO(cache) \ | ||
| 1565 | + (cache)->counter < (1<<ADAPTIVE_BACKOFF_BITS) | ||
| 1564 | 1566 | ||
| 1567 | + #define DECREMENT_ADAPTIVE_COUNTER(cache) \ | ||
| 1568 | + (cache)->counter -= (1<<ADAPTIVE_BACKOFF_BITS) | ||
| 1565 | 1569 | ||
| 1566 | 1570 | static int | |
| 1567 | 1571 | trace_function_entry(PyThreadState *tstate, _PyInterpreterFrame *frame) | |
@@ -2156,7 +2160,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 2156 | 2160 | ||
| 2157 | 2161 | TARGET(BINARY_SUBSCR_ADAPTIVE) { | |
| 2158 | 2162 | _PyBinarySubscrCache *cache = (_PyBinarySubscrCache *)next_instr; | |
| 2159 | - if (cache->counter == 0) { | ||
| 2163 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 2160 | 2164 | PyObject *sub = TOP(); | |
| 2161 | 2165 | PyObject *container = SECOND(); | |
| 2162 | 2166 | next_instr--; | |
@@ -2167,7 +2171,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 2167 | 2171 | } | |
| 2168 | 2172 | else { | |
| 2169 | 2173 | STAT_INC(BINARY_SUBSCR, deferred); | |
| 2170 | - cache->counter--; | ||
| 2174 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 2171 | 2175 | JUMP_TO_INSTRUCTION(BINARY_SUBSCR); | |
| 2172 | 2176 | } | |
| 2173 | 2177 | } | |
@@ -2321,7 +2325,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 2321 | 2325 | ||
| 2322 | 2326 | TARGET(STORE_SUBSCR_ADAPTIVE) { | |
| 2323 | 2327 | _PyStoreSubscrCache *cache = (_PyStoreSubscrCache *)next_instr; | |
| 2324 | - if (cache->counter == 0) { | ||
| 2328 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 2325 | 2329 | PyObject *sub = TOP(); | |
| 2326 | 2330 | PyObject *container = SECOND(); | |
| 2327 | 2331 | next_instr--; | |
@@ -2332,7 +2336,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 2332 | 2336 | } | |
| 2333 | 2337 | else { | |
| 2334 | 2338 | STAT_INC(STORE_SUBSCR, deferred); | |
| 2335 | - cache->counter--; | ||
| 2339 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 2336 | 2340 | JUMP_TO_INSTRUCTION(STORE_SUBSCR); | |
| 2337 | 2341 | } | |
| 2338 | 2342 | } | |
@@ -2815,15 +2819,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 2815 | 2819 | TARGET(UNPACK_SEQUENCE_ADAPTIVE) { | |
| 2816 | 2820 | assert(cframe.use_tracing == 0); | |
| 2817 | 2821 | _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr; | |
| 2818 | - if (cache->counter == 0) { | ||
| 2822 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 2819 | 2823 | PyObject *seq = TOP(); | |
| 2820 | 2824 | next_instr--; | |
| 2821 | 2825 | _Py_Specialize_UnpackSequence(seq, next_instr, oparg); | |
| 2822 | 2826 | NOTRACE_DISPATCH_SAME_OPARG(); | |
| 2823 | 2827 | } | |
| 2824 | 2828 | else { | |
| 2825 | 2829 | STAT_INC(UNPACK_SEQUENCE, deferred); | |
| 2826 | - cache->counter--; | ||
| 2830 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 2827 | 2831 | JUMP_TO_INSTRUCTION(UNPACK_SEQUENCE); | |
| 2828 | 2832 | } | |
| 2829 | 2833 | } | |
@@ -3056,7 +3060,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 3056 | 3060 | TARGET(LOAD_GLOBAL_ADAPTIVE) { | |
| 3057 | 3061 | assert(cframe.use_tracing == 0); | |
| 3058 | 3062 | _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr; | |
| 3059 | - if (cache->counter == 0) { | ||
| 3063 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 3060 | 3064 | PyObject *name = GETITEM(names, oparg>>1); | |
| 3061 | 3065 | next_instr--; | |
| 3062 | 3066 | if (_Py_Specialize_LoadGlobal(GLOBALS(), BUILTINS(), next_instr, name) < 0) { | |
@@ -3066,7 +3070,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 3066 | 3070 | } | |
| 3067 | 3071 | else { | |
| 3068 | 3072 | STAT_INC(LOAD_GLOBAL, deferred); | |
| 3069 | - cache->counter--; | ||
| 3073 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 3070 | 3074 | JUMP_TO_INSTRUCTION(LOAD_GLOBAL); | |
| 3071 | 3075 | } | |
| 3072 | 3076 | } | |
@@ -3480,7 +3484,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 3480 | 3484 | TARGET(LOAD_ATTR_ADAPTIVE) { | |
| 3481 | 3485 | assert(cframe.use_tracing == 0); | |
| 3482 | 3486 | _PyAttrCache *cache = (_PyAttrCache *)next_instr; | |
| 3483 | - if (cache->counter == 0) { | ||
| 3487 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 3484 | 3488 | PyObject *owner = TOP(); | |
| 3485 | 3489 | PyObject *name = GETITEM(names, oparg); | |
| 3486 | 3490 | next_instr--; | |
@@ -3491,7 +3495,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 3491 | 3495 | } | |
| 3492 | 3496 | else { | |
| 3493 | 3497 | STAT_INC(LOAD_ATTR, deferred); | |
| 3494 | - cache->counter--; | ||
| 3498 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 3495 | 3499 | JUMP_TO_INSTRUCTION(LOAD_ATTR); | |
| 3496 | 3500 | } | |
| 3497 | 3501 | } | |
@@ -3589,7 +3593,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 3589 | 3593 | TARGET(STORE_ATTR_ADAPTIVE) { | |
| 3590 | 3594 | assert(cframe.use_tracing == 0); | |
| 3591 | 3595 | _PyAttrCache *cache = (_PyAttrCache *)next_instr; | |
| 3592 | - if (cache->counter == 0) { | ||
| 3596 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 3593 | 3597 | PyObject *owner = TOP(); | |
| 3594 | 3598 | PyObject *name = GETITEM(names, oparg); | |
| 3595 | 3599 | next_instr--; | |
@@ -3600,7 +3604,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 3600 | 3604 | } | |
| 3601 | 3605 | else { | |
| 3602 | 3606 | STAT_INC(STORE_ATTR, deferred); | |
| 3603 | - cache->counter--; | ||
| 3607 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 3604 | 3608 | JUMP_TO_INSTRUCTION(STORE_ATTR); | |
| 3605 | 3609 | } | |
| 3606 | 3610 | } | |
@@ -3719,7 +3723,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 3719 | 3723 | TARGET(COMPARE_OP_ADAPTIVE) { | |
| 3720 | 3724 | assert(cframe.use_tracing == 0); | |
| 3721 | 3725 | _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; | |
| 3722 | - if (cache->counter == 0) { | ||
| 3726 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 3723 | 3727 | PyObject *right = TOP(); | |
| 3724 | 3728 | PyObject *left = SECOND(); | |
| 3725 | 3729 | next_instr--; | |
@@ -3728,7 +3732,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 3728 | 3732 | } | |
| 3729 | 3733 | else { | |
| 3730 | 3734 | STAT_INC(COMPARE_OP, deferred); | |
| 3731 | - cache->counter--; | ||
| 3735 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 3732 | 3736 | JUMP_TO_INSTRUCTION(COMPARE_OP); | |
| 3733 | 3737 | } | |
| 3734 | 3738 | } | |
@@ -4526,7 +4530,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 4526 | 4530 | TARGET(LOAD_METHOD_ADAPTIVE) { | |
| 4527 | 4531 | assert(cframe.use_tracing == 0); | |
| 4528 | 4532 | _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; | |
| 4529 | - if (cache->counter == 0) { | ||
| 4533 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 4530 | 4534 | PyObject *owner = TOP(); | |
| 4531 | 4535 | PyObject *name = GETITEM(names, oparg); | |
| 4532 | 4536 | next_instr--; | |
@@ -4537,7 +4541,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 4537 | 4541 | } | |
| 4538 | 4542 | else { | |
| 4539 | 4543 | STAT_INC(LOAD_METHOD, deferred); | |
| 4540 | - cache->counter--; | ||
| 4544 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 4541 | 4545 | JUMP_TO_INSTRUCTION(LOAD_METHOD); | |
| 4542 | 4546 | } | |
| 4543 | 4547 | } | |
@@ -4775,7 +4779,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 4775 | 4779 | ||
| 4776 | 4780 | TARGET(CALL_ADAPTIVE) { | |
| 4777 | 4781 | _PyCallCache *cache = (_PyCallCache *)next_instr; | |
| 4778 | - if (cache->counter == 0) { | ||
| 4782 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 4779 | 4783 | next_instr--; | |
| 4780 | 4784 | int is_meth = is_method(stack_pointer, oparg); | |
| 4781 | 4785 | int nargs = oparg + is_meth; | |
@@ -4789,7 +4793,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 4789 | 4793 | } | |
| 4790 | 4794 | else { | |
| 4791 | 4795 | STAT_INC(CALL, deferred); | |
| 4792 | - cache->counter--; | ||
| 4796 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 4793 | 4797 | goto call_function; | |
| 4794 | 4798 | } | |
| 4795 | 4799 | } | |
@@ -5521,7 +5525,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 5521 | 5525 | TARGET(BINARY_OP_ADAPTIVE) { | |
| 5522 | 5526 | assert(cframe.use_tracing == 0); | |
| 5523 | 5527 | _PyBinaryOpCache *cache = (_PyBinaryOpCache *)next_instr; | |
| 5524 | - if (cache->counter == 0) { | ||
| 5528 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 5525 | 5529 | PyObject *lhs = SECOND(); | |
| 5526 | 5530 | PyObject *rhs = TOP(); | |
| 5527 | 5531 | next_instr--; | |
@@ -5530,7 +5534,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 5530 | 5534 | } | |
| 5531 | 5535 | else { | |
| 5532 | 5536 | STAT_INC(BINARY_OP, deferred); | |
| 5533 | - cache->counter--; | ||
| 5537 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 5534 | 5538 | JUMP_TO_INSTRUCTION(BINARY_OP); | |
| 5535 | 5539 | } | |
| 5536 | 5540 | } | |
@@ -5658,7 +5662,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 5658 | 5662 | assert(adaptive_opcode); | |
| 5659 | 5663 | _Py_SET_OPCODE(next_instr[-1], adaptive_opcode); | |
| 5660 | 5664 | STAT_INC(opcode, deopt); | |
| 5661 | - *counter = ADAPTIVE_CACHE_BACKOFF; | ||
| 5665 | + *counter = adaptive_counter_start(); | ||
| 5662 | 5666 | } | |
| 5663 | 5667 | next_instr--; | |
| 5664 | 5668 | DISPATCH_GOTO(); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments