| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -233,9 +233,6 @@ extern void _PyLineTable_InitAddressRange( | |||
| 233 | 233 | extern int _PyLineTable_NextAddressRange(PyCodeAddressRange *range); | |
| 234 | 234 | extern int _PyLineTable_PreviousAddressRange(PyCodeAddressRange *range); | |
| 235 | 235 | ||
| 236 | - | ||
| 237 | - #define ADAPTIVE_CACHE_BACKOFF 64 | ||
| 238 | - | ||
| 239 | 236 | /* Specialization functions */ | |
| 240 | 237 | ||
| 241 | 238 | extern int _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, | |
@@ -475,6 +472,50 @@ write_location_entry_start(uint8_t *ptr, int code, int length) | |||
| 475 | 472 | } | |
| 476 | 473 | ||
| 477 | 474 | ||
| 475 | + /** Counters | ||
| 476 | + * The first 16-bit value in each inline cache is a counter. | ||
| 477 | + * When counting misses, the counter is treated as a simple unsigned value. | ||
| 478 | + * | ||
| 479 | + * When counting executions until the next specialization attempt, | ||
| 480 | + * exponential backoff is used to reduce the number of specialization failures. | ||
| 481 | + * The high 12 bits store the counter, the low 4 bits store the backoff exponent. | ||
| 482 | + * On a specialization failure, the backoff exponent is incremented and the | ||
| 483 | + * counter set to (2**backoff - 1). | ||
| 484 | + * Backoff == 6 -> starting counter == 63, backoff == 10 -> starting counter == 1023. | ||
| 485 | + */ | ||
| 486 | + | ||
| 487 | + /* With a 16-bit counter, we have 12 bits for the counter value, and 4 bits for the backoff */ | ||
| 488 | + #define ADAPTIVE_BACKOFF_BITS 4 | ||
| 489 | + /* The initial counter value is 31 == 2**ADAPTIVE_BACKOFF_START - 1 */ | ||
| 490 | + #define ADAPTIVE_BACKOFF_START 5 | ||
| 491 | + | ||
| 492 | + #define MAX_BACKOFF_VALUE (16 - ADAPTIVE_BACKOFF_BITS) | ||
| 493 | + | ||
| 494 | + | ||
| 495 | + static inline uint16_t | ||
| 496 | + adaptive_counter_bits(int value, int backoff) { | ||
| 497 | + return (value << ADAPTIVE_BACKOFF_BITS) | | ||
| 498 | + (backoff & ((1<<ADAPTIVE_BACKOFF_BITS)-1)); | ||
| 499 | + } | ||
| 500 | + | ||
| 501 | + static inline uint16_t | ||
| 502 | + adaptive_counter_start(void) { | ||
| 503 | + unsigned int value = (1 << ADAPTIVE_BACKOFF_START) - 1; | ||
| 504 | + return adaptive_counter_bits(value, ADAPTIVE_BACKOFF_START); | ||
| 505 | + } | ||
| 506 | + | ||
| 507 | + static inline uint16_t | ||
| 508 | + adaptive_counter_backoff(uint16_t counter) { | ||
| 509 | + unsigned int backoff = counter & ((1<<ADAPTIVE_BACKOFF_BITS)-1); | ||
| 510 | + backoff++; | ||
| 511 | + if (backoff > MAX_BACKOFF_VALUE) { | ||
| 512 | + backoff = MAX_BACKOFF_VALUE; | ||
| 513 | + } | ||
| 514 | + unsigned int value = (1 << backoff) - 1; | ||
| 515 | + return adaptive_counter_bits(value, backoff); | ||
| 516 | + } | ||
| 517 | + | ||
| 518 | + | ||
| 478 | 519 | #ifdef __cplusplus | |
| 479 | 520 | } | |
| 480 | 521 | #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 | |
|---|---|---|---|
@@ -1560,7 +1560,11 @@ eval_frame_handle_pending(PyThreadState *tstate) | |||
| 1560 | 1560 | dtrace_function_entry(frame); \ | |
| 1561 | 1561 | } | |
| 1562 | 1562 | ||
| 1563 | + #define ADAPTIVE_COUNTER_IS_ZERO(cache) \ | ||
| 1564 | + (cache)->counter < (1<<ADAPTIVE_BACKOFF_BITS) | ||
| 1563 | 1565 | ||
| 1566 | + #define DECREMENT_ADAPTIVE_COUNTER(cache) \ | ||
| 1567 | + (cache)->counter -= (1<<ADAPTIVE_BACKOFF_BITS) | ||
| 1564 | 1568 | ||
| 1565 | 1569 | static int | |
| 1566 | 1570 | trace_function_entry(PyThreadState *tstate, _PyInterpreterFrame *frame) | |
@@ -2155,7 +2159,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 2155 | 2159 | ||
| 2156 | 2160 | TARGET(BINARY_SUBSCR_ADAPTIVE) { | |
| 2157 | 2161 | _PyBinarySubscrCache *cache = (_PyBinarySubscrCache *)next_instr; | |
| 2158 | - if (cache->counter == 0) { | ||
| 2162 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 2159 | 2163 | PyObject *sub = TOP(); | |
| 2160 | 2164 | PyObject *container = SECOND(); | |
| 2161 | 2165 | next_instr--; | |
@@ -2166,7 +2170,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 2166 | 2170 | } | |
| 2167 | 2171 | else { | |
| 2168 | 2172 | STAT_INC(BINARY_SUBSCR, deferred); | |
| 2169 | - cache->counter--; | ||
| 2173 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 2170 | 2174 | JUMP_TO_INSTRUCTION(BINARY_SUBSCR); | |
| 2171 | 2175 | } | |
| 2172 | 2176 | } | |
@@ -2320,7 +2324,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 2320 | 2324 | ||
| 2321 | 2325 | TARGET(STORE_SUBSCR_ADAPTIVE) { | |
| 2322 | 2326 | _PyStoreSubscrCache *cache = (_PyStoreSubscrCache *)next_instr; | |
| 2323 | - if (cache->counter == 0) { | ||
| 2327 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 2324 | 2328 | PyObject *sub = TOP(); | |
| 2325 | 2329 | PyObject *container = SECOND(); | |
| 2326 | 2330 | next_instr--; | |
@@ -2331,7 +2335,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 2331 | 2335 | } | |
| 2332 | 2336 | else { | |
| 2333 | 2337 | STAT_INC(STORE_SUBSCR, deferred); | |
| 2334 | - cache->counter--; | ||
| 2338 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 2335 | 2339 | JUMP_TO_INSTRUCTION(STORE_SUBSCR); | |
| 2336 | 2340 | } | |
| 2337 | 2341 | } | |
@@ -2813,15 +2817,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 2813 | 2817 | TARGET(UNPACK_SEQUENCE_ADAPTIVE) { | |
| 2814 | 2818 | assert(cframe.use_tracing == 0); | |
| 2815 | 2819 | _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr; | |
| 2816 | - if (cache->counter == 0) { | ||
| 2820 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 2817 | 2821 | PyObject *seq = TOP(); | |
| 2818 | 2822 | next_instr--; | |
| 2819 | 2823 | _Py_Specialize_UnpackSequence(seq, next_instr, oparg); | |
| 2820 | 2824 | NOTRACE_DISPATCH_SAME_OPARG(); | |
| 2821 | 2825 | } | |
| 2822 | 2826 | else { | |
| 2823 | 2827 | STAT_INC(UNPACK_SEQUENCE, deferred); | |
| 2824 | - cache->counter--; | ||
| 2828 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 2825 | 2829 | JUMP_TO_INSTRUCTION(UNPACK_SEQUENCE); | |
| 2826 | 2830 | } | |
| 2827 | 2831 | } | |
@@ -3054,7 +3058,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 3054 | 3058 | TARGET(LOAD_GLOBAL_ADAPTIVE) { | |
| 3055 | 3059 | assert(cframe.use_tracing == 0); | |
| 3056 | 3060 | _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr; | |
| 3057 | - if (cache->counter == 0) { | ||
| 3061 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 3058 | 3062 | PyObject *name = GETITEM(names, oparg>>1); | |
| 3059 | 3063 | next_instr--; | |
| 3060 | 3064 | if (_Py_Specialize_LoadGlobal(GLOBALS(), BUILTINS(), next_instr, name) < 0) { | |
@@ -3064,7 +3068,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 3064 | 3068 | } | |
| 3065 | 3069 | else { | |
| 3066 | 3070 | STAT_INC(LOAD_GLOBAL, deferred); | |
| 3067 | - cache->counter--; | ||
| 3071 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 3068 | 3072 | JUMP_TO_INSTRUCTION(LOAD_GLOBAL); | |
| 3069 | 3073 | } | |
| 3070 | 3074 | } | |
@@ -3478,7 +3482,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 3478 | 3482 | TARGET(LOAD_ATTR_ADAPTIVE) { | |
| 3479 | 3483 | assert(cframe.use_tracing == 0); | |
| 3480 | 3484 | _PyAttrCache *cache = (_PyAttrCache *)next_instr; | |
| 3481 | - if (cache->counter == 0) { | ||
| 3485 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 3482 | 3486 | PyObject *owner = TOP(); | |
| 3483 | 3487 | PyObject *name = GETITEM(names, oparg); | |
| 3484 | 3488 | next_instr--; | |
@@ -3489,7 +3493,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 3489 | 3493 | } | |
| 3490 | 3494 | else { | |
| 3491 | 3495 | STAT_INC(LOAD_ATTR, deferred); | |
| 3492 | - cache->counter--; | ||
| 3496 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 3493 | 3497 | JUMP_TO_INSTRUCTION(LOAD_ATTR); | |
| 3494 | 3498 | } | |
| 3495 | 3499 | } | |
@@ -3587,7 +3591,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 3587 | 3591 | TARGET(STORE_ATTR_ADAPTIVE) { | |
| 3588 | 3592 | assert(cframe.use_tracing == 0); | |
| 3589 | 3593 | _PyAttrCache *cache = (_PyAttrCache *)next_instr; | |
| 3590 | - if (cache->counter == 0) { | ||
| 3594 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 3591 | 3595 | PyObject *owner = TOP(); | |
| 3592 | 3596 | PyObject *name = GETITEM(names, oparg); | |
| 3593 | 3597 | next_instr--; | |
@@ -3598,7 +3602,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 3598 | 3602 | } | |
| 3599 | 3603 | else { | |
| 3600 | 3604 | STAT_INC(STORE_ATTR, deferred); | |
| 3601 | - cache->counter--; | ||
| 3605 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 3602 | 3606 | JUMP_TO_INSTRUCTION(STORE_ATTR); | |
| 3603 | 3607 | } | |
| 3604 | 3608 | } | |
@@ -3717,7 +3721,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 3717 | 3721 | TARGET(COMPARE_OP_ADAPTIVE) { | |
| 3718 | 3722 | assert(cframe.use_tracing == 0); | |
| 3719 | 3723 | _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; | |
| 3720 | - if (cache->counter == 0) { | ||
| 3724 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 3721 | 3725 | PyObject *right = TOP(); | |
| 3722 | 3726 | PyObject *left = SECOND(); | |
| 3723 | 3727 | next_instr--; | |
@@ -3726,7 +3730,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 3726 | 3730 | } | |
| 3727 | 3731 | else { | |
| 3728 | 3732 | STAT_INC(COMPARE_OP, deferred); | |
| 3729 | - cache->counter--; | ||
| 3733 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 3730 | 3734 | JUMP_TO_INSTRUCTION(COMPARE_OP); | |
| 3731 | 3735 | } | |
| 3732 | 3736 | } | |
@@ -4524,7 +4528,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 4524 | 4528 | TARGET(LOAD_METHOD_ADAPTIVE) { | |
| 4525 | 4529 | assert(cframe.use_tracing == 0); | |
| 4526 | 4530 | _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; | |
| 4527 | - if (cache->counter == 0) { | ||
| 4531 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 4528 | 4532 | PyObject *owner = TOP(); | |
| 4529 | 4533 | PyObject *name = GETITEM(names, oparg); | |
| 4530 | 4534 | next_instr--; | |
@@ -4535,7 +4539,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 4535 | 4539 | } | |
| 4536 | 4540 | else { | |
| 4537 | 4541 | STAT_INC(LOAD_METHOD, deferred); | |
| 4538 | - cache->counter--; | ||
| 4542 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 4539 | 4543 | JUMP_TO_INSTRUCTION(LOAD_METHOD); | |
| 4540 | 4544 | } | |
| 4541 | 4545 | } | |
@@ -4816,7 +4820,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 4816 | 4820 | ||
| 4817 | 4821 | TARGET(CALL_ADAPTIVE) { | |
| 4818 | 4822 | _PyCallCache *cache = (_PyCallCache *)next_instr; | |
| 4819 | - if (cache->counter == 0) { | ||
| 4823 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 4820 | 4824 | next_instr--; | |
| 4821 | 4825 | int is_meth = is_method(stack_pointer, oparg); | |
| 4822 | 4826 | int nargs = oparg + is_meth; | |
@@ -4830,7 +4834,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 4830 | 4834 | } | |
| 4831 | 4835 | else { | |
| 4832 | 4836 | STAT_INC(CALL, deferred); | |
| 4833 | - cache->counter--; | ||
| 4837 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 4834 | 4838 | goto call_function; | |
| 4835 | 4839 | } | |
| 4836 | 4840 | } | |
@@ -5561,7 +5565,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 5561 | 5565 | TARGET(BINARY_OP_ADAPTIVE) { | |
| 5562 | 5566 | assert(cframe.use_tracing == 0); | |
| 5563 | 5567 | _PyBinaryOpCache *cache = (_PyBinaryOpCache *)next_instr; | |
| 5564 | - if (cache->counter == 0) { | ||
| 5568 | + if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { | ||
| 5565 | 5569 | PyObject *lhs = SECOND(); | |
| 5566 | 5570 | PyObject *rhs = TOP(); | |
| 5567 | 5571 | next_instr--; | |
@@ -5570,7 +5574,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 5570 | 5574 | } | |
| 5571 | 5575 | else { | |
| 5572 | 5576 | STAT_INC(BINARY_OP, deferred); | |
| 5573 | - cache->counter--; | ||
| 5577 | + DECREMENT_ADAPTIVE_COUNTER(cache); | ||
| 5574 | 5578 | JUMP_TO_INSTRUCTION(BINARY_OP); | |
| 5575 | 5579 | } | |
| 5576 | 5580 | } | |
@@ -5698,7 +5702,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int | |||
| 5698 | 5702 | assert(adaptive_opcode); | |
| 5699 | 5703 | _Py_SET_OPCODE(next_instr[-1], adaptive_opcode); | |
| 5700 | 5704 | STAT_INC(opcode, deopt); | |
| 5701 | - *counter = ADAPTIVE_CACHE_BACKOFF; | ||
| 5705 | + *counter = adaptive_counter_start(); | ||
| 5702 | 5706 | } | |
| 5703 | 5707 | next_instr--; | |
| 5704 | 5708 | DISPATCH_GOTO(); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments