From 86de63aa1f8cdc53550d750547c12c11cb181bc8 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Sat, 15 Nov 2025 13:34:42 +0300 Subject: [PATCH 01/21] Some refactor --- Include/internal/pycore_backoff.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index 71066f1bd9f19b..6b287568a3f80a 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -32,8 +32,10 @@ extern "C" { */ #define BACKOFF_BITS 4 +#define BACKOFF_MASK 0xF #define MAX_BACKOFF 12 #define UNREACHABLE_BACKOFF 15 +#define MAX_VALUE 0xFFF static inline bool is_unreachable_backoff_counter(_Py_BackoffCounter counter) @@ -44,8 +46,8 @@ is_unreachable_backoff_counter(_Py_BackoffCounter counter) static inline _Py_BackoffCounter make_backoff_counter(uint16_t value, uint16_t backoff) { - assert(backoff <= 15); - assert(value <= 0xFFF); + assert(backoff <= UNREACHABLE_BACKOFF); + assert(value <= MAX_VALUE); _Py_BackoffCounter result; result.value_and_backoff = (value << BACKOFF_BITS) | backoff; return result; @@ -63,7 +65,7 @@ static inline _Py_BackoffCounter restart_backoff_counter(_Py_BackoffCounter counter) { assert(!is_unreachable_backoff_counter(counter)); - int backoff = counter.value_and_backoff & 15; + int backoff = counter.value_and_backoff & BACKOFF_MASK; if (backoff < MAX_BACKOFF) { return make_backoff_counter((1 << (backoff + 1)) - 1, backoff + 1); } From 4eb3231710cdeaf9a0dcf48b35784bb193a040cf Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Sat, 15 Nov 2025 13:57:07 +0300 Subject: [PATCH 02/21] Changes in backoff bit pattern --- Include/internal/pycore_backoff.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index 6b287568a3f80a..d9d9efee96975f 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -22,20 +22,20 @@ extern "C" { Another use is for the Tier 2 optimizer to decide when to create a new Tier 2 trace (executor). Again, exponential backoff is used. - The 16-bit counter is structured as a 12-bit unsigned 'value' - and a 4-bit 'backoff' field. When resetting the counter, the + The 16-bit counter is structured as a 13-bit unsigned 'value' + and a 3-bit 'backoff' field. When resetting the counter, the backoff field is incremented (until it reaches a limit) and the - value is set to a bit mask representing the value 2**backoff - 1. - The maximum backoff is 12 (the number of bits in the value). + value is set to a bit mask representing the value 2**(2*backoff+1) - 1. + The maximum backoff is 6, since 7 is an UNREACHABLE_BACKOFF. There is an exceptional value which must not be updated, 0xFFFF. */ -#define BACKOFF_BITS 4 -#define BACKOFF_MASK 0xF -#define MAX_BACKOFF 12 -#define UNREACHABLE_BACKOFF 15 -#define MAX_VALUE 0xFFF +#define BACKOFF_BITS 3 +#define BACKOFF_MASK 7 +#define MAX_BACKOFF 6 +#define UNREACHABLE_BACKOFF 7 +#define MAX_VALUE 0x1FFF static inline bool is_unreachable_backoff_counter(_Py_BackoffCounter counter) @@ -67,10 +67,10 @@ restart_backoff_counter(_Py_BackoffCounter counter) assert(!is_unreachable_backoff_counter(counter)); int backoff = counter.value_and_backoff & BACKOFF_MASK; if (backoff < MAX_BACKOFF) { - return make_backoff_counter((1 << (backoff + 1)) - 1, backoff + 1); + return make_backoff_counter((1 << (2 * backoff + 3)) - 1, backoff + 1); } else { - return make_backoff_counter((1 << MAX_BACKOFF) - 1, MAX_BACKOFF); + return make_backoff_counter((1 << (2 * MAX_BACKOFF + 1)) - 1, MAX_BACKOFF); } } @@ -115,7 +115,7 @@ trigger_backoff_counter(void) // as we always end up tracing the loop iteration's // exhaustion iteration. Which aborts our current tracer. #define JUMP_BACKWARD_INITIAL_VALUE 4000 -#define JUMP_BACKWARD_INITIAL_BACKOFF 12 +#define JUMP_BACKWARD_INITIAL_BACKOFF 6 static inline _Py_BackoffCounter initial_jump_backoff_counter(void) { @@ -128,7 +128,7 @@ initial_jump_backoff_counter(void) * otherwise when a side exit warms up we may construct * a new trace before the Tier 1 code has properly re-specialized. */ #define SIDE_EXIT_INITIAL_VALUE 4000 -#define SIDE_EXIT_INITIAL_BACKOFF 12 +#define SIDE_EXIT_INITIAL_BACKOFF 6 static inline _Py_BackoffCounter initial_temperature_backoff_counter(void) From 25816298feef931d941f4d21624760188ff8c20d Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Sat, 15 Nov 2025 14:04:38 +0300 Subject: [PATCH 03/21] Add NEWS --- .../2025-11-15-14-04-35.gh-issue-141589.VfdMDD.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-11-15-14-04-35.gh-issue-141589.VfdMDD.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-15-14-04-35.gh-issue-141589.VfdMDD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-15-14-04-35.gh-issue-141589.VfdMDD.rst new file mode 100644 index 00000000000000..a1637c6f2eb173 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-15-14-04-35.gh-issue-141589.VfdMDD.rst @@ -0,0 +1,2 @@ +Change ``backoff counter`` to use powers of 4 instead of powers of 2. Patch +by Mikhail Efimov. From 96de415869221eff5a3c70d50b31dbccde9515ec Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Sat, 15 Nov 2025 14:10:35 +0300 Subject: [PATCH 04/21] NEWS fix --- .../2025-11-15-14-04-35.gh-issue-141589.VfdMDD.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-15-14-04-35.gh-issue-141589.VfdMDD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-15-14-04-35.gh-issue-141589.VfdMDD.rst index a1637c6f2eb173..10c8d268e42994 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-15-14-04-35.gh-issue-141589.VfdMDD.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-15-14-04-35.gh-issue-141589.VfdMDD.rst @@ -1,2 +1,2 @@ -Change ``backoff counter`` to use powers of 4 instead of powers of 2. Patch -by Mikhail Efimov. +Change ``backoff counter`` to use powers of 4 instead of powers of 2. +This allows to support values up to 8191. Patch by Mikhail Efimov. From ce3e14f251b5f4c59cee318eb4c38f8443321fcb Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Sat, 15 Nov 2025 15:17:22 +0300 Subject: [PATCH 05/21] Change ITEMS count in test --- Lib/test/test_opcache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py index c7eea75117de8c..4113b79ef5c80b 100644 --- a/Lib/test/test_opcache.py +++ b/Lib/test/test_opcache.py @@ -590,7 +590,7 @@ def make_deferred_ref_count_obj(): class TestRacesDoNotCrash(TestBase): # Careful with these. Bigger numbers have a higher chance of catching bugs, # but you can also burn through a *ton* of type/dict/function versions: - ITEMS = 1000 + ITEMS = 1400 LOOPS = 4 WRITERS = 2 From e17b195eadfdddc39e89233ae7cfdda4e5932c05 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 19 Nov 2025 16:28:08 +0300 Subject: [PATCH 06/21] Refactor restart_backoff_counter --- Include/internal/pycore_backoff.h | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index d9d9efee96975f..b7d5e1479dfe4c 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -65,13 +65,11 @@ static inline _Py_BackoffCounter restart_backoff_counter(_Py_BackoffCounter counter) { assert(!is_unreachable_backoff_counter(counter)); - int backoff = counter.value_and_backoff & BACKOFF_MASK; - if (backoff < MAX_BACKOFF) { - return make_backoff_counter((1 << (2 * backoff + 3)) - 1, backoff + 1); - } - else { - return make_backoff_counter((1 << (2 * MAX_BACKOFF + 1)) - 1, MAX_BACKOFF); - } + uint16_t backoff = counter.value_and_backoff & BACKOFF_MASK; + assert(backoff <= MAX_BACKOFF); + backoff = (backoff == MAX_BACKOFF) ? backoff : backoff + 1; + uint16_t value = (1 << (2 * backoff + 1)) - 1; + return make_backoff_counter(value, backoff); } static inline _Py_BackoffCounter From 44b4eb889186f8b727d1341c6cc73f63e8736885 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 19 Nov 2025 16:29:43 +0300 Subject: [PATCH 07/21] Remove unneed assertion and function --- Include/internal/pycore_backoff.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index b7d5e1479dfe4c..f08bebe5168cb0 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -37,12 +37,6 @@ extern "C" { #define UNREACHABLE_BACKOFF 7 #define MAX_VALUE 0x1FFF -static inline bool -is_unreachable_backoff_counter(_Py_BackoffCounter counter) -{ - return counter.value_and_backoff == UNREACHABLE_BACKOFF; -} - static inline _Py_BackoffCounter make_backoff_counter(uint16_t value, uint16_t backoff) { @@ -64,7 +58,6 @@ forge_backoff_counter(uint16_t counter) static inline _Py_BackoffCounter restart_backoff_counter(_Py_BackoffCounter counter) { - assert(!is_unreachable_backoff_counter(counter)); uint16_t backoff = counter.value_and_backoff & BACKOFF_MASK; assert(backoff <= MAX_BACKOFF); backoff = (backoff == MAX_BACKOFF) ? backoff : backoff + 1; From 6d443334c47f99c93789411e9ea09956c4335ab0 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 19 Nov 2025 16:55:24 +0300 Subject: [PATCH 08/21] Use lookup_table --- Include/internal/pycore_backoff.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index f08bebe5168cb0..d96815a32c371c 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -37,6 +37,9 @@ extern "C" { #define UNREACHABLE_BACKOFF 7 #define MAX_VALUE 0x1FFF +// We only use values x such that x + 1 is prime. +static const int lookup_table[] = {1, 6, 30, 126, 508, 2052, 8190, 8190}; + static inline _Py_BackoffCounter make_backoff_counter(uint16_t value, uint16_t backoff) { @@ -61,8 +64,7 @@ restart_backoff_counter(_Py_BackoffCounter counter) uint16_t backoff = counter.value_and_backoff & BACKOFF_MASK; assert(backoff <= MAX_BACKOFF); backoff = (backoff == MAX_BACKOFF) ? backoff : backoff + 1; - uint16_t value = (1 << (2 * backoff + 1)) - 1; - return make_backoff_counter(value, backoff); + return make_backoff_counter(lookup_table[backoff], backoff); } static inline _Py_BackoffCounter From 0e057129d492549d2ea79927dc86fe3f292b17db Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 19 Nov 2025 17:25:59 +0300 Subject: [PATCH 09/21] Use MAKE_BACKOFF_COUNTER macro --- Include/internal/pycore_backoff.h | 37 ++++++++++++++++--------------- Include/internal/pycore_code.h | 13 ++++------- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index d96815a32c371c..cfa46bfcb4b0b4 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -35,20 +35,21 @@ extern "C" { #define BACKOFF_MASK 7 #define MAX_BACKOFF 6 #define UNREACHABLE_BACKOFF 7 -#define MAX_VALUE 0x1FFF -// We only use values x such that x + 1 is prime. -static const int lookup_table[] = {1, 6, 30, 126, 508, 2052, 8190, 8190}; - -static inline _Py_BackoffCounter -make_backoff_counter(uint16_t value, uint16_t backoff) -{ - assert(backoff <= UNREACHABLE_BACKOFF); - assert(value <= MAX_VALUE); - _Py_BackoffCounter result; - result.value_and_backoff = (value << BACKOFF_BITS) | backoff; - return result; -} +#define MAKE_BACKOFF_COUNTER(value, backoff) \ + ((_Py_BackoffCounter){ .value_and_backoff = (value << BACKOFF_BITS) | backoff }) + +// We only use values x and backoffs b such that +// x + 1 is prime and is near to 2**(2*b+1). +static const _Py_BackoffCounter backoff_counter_table[] = { + MAKE_BACKOFF_COUNTER(1, 0), + MAKE_BACKOFF_COUNTER(6, 1), + MAKE_BACKOFF_COUNTER(30, 2), + MAKE_BACKOFF_COUNTER(126, 3), + MAKE_BACKOFF_COUNTER(508, 4), + MAKE_BACKOFF_COUNTER(2052, 5), + MAKE_BACKOFF_COUNTER(8190, 6), +}; static inline _Py_BackoffCounter forge_backoff_counter(uint16_t counter) @@ -63,8 +64,8 @@ restart_backoff_counter(_Py_BackoffCounter counter) { uint16_t backoff = counter.value_and_backoff & BACKOFF_MASK; assert(backoff <= MAX_BACKOFF); - backoff = (backoff == MAX_BACKOFF) ? backoff : backoff + 1; - return make_backoff_counter(lookup_table[backoff], backoff); + backoff = (backoff >= MAX_BACKOFF) ? MAX_BACKOFF : backoff + 1; + return backoff_counter_table[backoff]; } static inline _Py_BackoffCounter @@ -112,7 +113,7 @@ trigger_backoff_counter(void) static inline _Py_BackoffCounter initial_jump_backoff_counter(void) { - return make_backoff_counter(JUMP_BACKWARD_INITIAL_VALUE, + return MAKE_BACKOFF_COUNTER(JUMP_BACKWARD_INITIAL_VALUE, JUMP_BACKWARD_INITIAL_BACKOFF); } @@ -126,7 +127,7 @@ initial_jump_backoff_counter(void) static inline _Py_BackoffCounter initial_temperature_backoff_counter(void) { - return make_backoff_counter(SIDE_EXIT_INITIAL_VALUE, + return MAKE_BACKOFF_COUNTER(SIDE_EXIT_INITIAL_VALUE, SIDE_EXIT_INITIAL_BACKOFF); } @@ -134,7 +135,7 @@ initial_temperature_backoff_counter(void) static inline _Py_BackoffCounter initial_unreachable_backoff_counter(void) { - return make_backoff_counter(0, UNREACHABLE_BACKOFF); + return MAKE_BACKOFF_COUNTER(0, UNREACHABLE_BACKOFF); } #ifdef __cplusplus diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index cb9c0aa27a1785..b5dfd471b5d8ce 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -466,21 +466,16 @@ write_location_entry_start(uint8_t *ptr, int code, int length) # error "Cold exit value should be larger than adaptive cooldown value" #endif -static inline _Py_BackoffCounter -adaptive_counter_bits(uint16_t value, uint16_t backoff) { - return make_backoff_counter(value, backoff); -} - static inline _Py_BackoffCounter adaptive_counter_warmup(void) { - return adaptive_counter_bits(ADAPTIVE_WARMUP_VALUE, - ADAPTIVE_WARMUP_BACKOFF); + return MAKE_BACKOFF_COUNTER(ADAPTIVE_WARMUP_VALUE, + ADAPTIVE_WARMUP_BACKOFF); } static inline _Py_BackoffCounter adaptive_counter_cooldown(void) { - return adaptive_counter_bits(ADAPTIVE_COOLDOWN_VALUE, - ADAPTIVE_COOLDOWN_BACKOFF); + return MAKE_BACKOFF_COUNTER(ADAPTIVE_COOLDOWN_VALUE, + ADAPTIVE_COOLDOWN_BACKOFF); } static inline _Py_BackoffCounter From f3c92874741e6c04d001cc673718941ddd3577a2 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 19 Nov 2025 17:28:42 +0300 Subject: [PATCH 10/21] Comment change --- Include/internal/pycore_backoff.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index cfa46bfcb4b0b4..3f43b398260f7a 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -40,7 +40,7 @@ extern "C" { ((_Py_BackoffCounter){ .value_and_backoff = (value << BACKOFF_BITS) | backoff }) // We only use values x and backoffs b such that -// x + 1 is prime and is near to 2**(2*b+1). +// x + 1 is near to 2**(2*b+1) and x + 1 is prime. static const _Py_BackoffCounter backoff_counter_table[] = { MAKE_BACKOFF_COUNTER(1, 0), MAKE_BACKOFF_COUNTER(6, 1), From e16b368f12b028462aba73b915a4324c599656cc Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 19 Nov 2025 17:37:40 +0300 Subject: [PATCH 11/21] Fix Windows build: remove const keyword --- Include/internal/pycore_backoff.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index 3f43b398260f7a..ee2f1eb7ac1d38 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -41,7 +41,7 @@ extern "C" { // We only use values x and backoffs b such that // x + 1 is near to 2**(2*b+1) and x + 1 is prime. -static const _Py_BackoffCounter backoff_counter_table[] = { +static _Py_BackoffCounter backoff_counter_table[] = { MAKE_BACKOFF_COUNTER(1, 0), MAKE_BACKOFF_COUNTER(6, 1), MAKE_BACKOFF_COUNTER(30, 2), From 6b1fe6674f01585faec273e8b2ab84ebf6ddfcd0 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 19 Nov 2025 17:46:38 +0300 Subject: [PATCH 12/21] Simplify obtaining next backoff --- Include/internal/pycore_backoff.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index ee2f1eb7ac1d38..f155236980843f 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -41,14 +41,16 @@ extern "C" { // We only use values x and backoffs b such that // x + 1 is near to 2**(2*b+1) and x + 1 is prime. -static _Py_BackoffCounter backoff_counter_table[] = { - MAKE_BACKOFF_COUNTER(1, 0), +static _Py_BackoffCounter backoff_counter_next_table[] = { MAKE_BACKOFF_COUNTER(6, 1), MAKE_BACKOFF_COUNTER(30, 2), MAKE_BACKOFF_COUNTER(126, 3), MAKE_BACKOFF_COUNTER(508, 4), MAKE_BACKOFF_COUNTER(2052, 5), MAKE_BACKOFF_COUNTER(8190, 6), + // We use the same backoff counter for all backoffs >= MAX_BACKOFF. + MAKE_BACKOFF_COUNTER(8190, 6), + MAKE_BACKOFF_COUNTER(8190, 6), }; static inline _Py_BackoffCounter @@ -64,8 +66,7 @@ restart_backoff_counter(_Py_BackoffCounter counter) { uint16_t backoff = counter.value_and_backoff & BACKOFF_MASK; assert(backoff <= MAX_BACKOFF); - backoff = (backoff >= MAX_BACKOFF) ? MAX_BACKOFF : backoff + 1; - return backoff_counter_table[backoff]; + return backoff_counter_next_table[backoff]; } static inline _Py_BackoffCounter From 2600236851d82903b0b8a83f9fa7d9dae2ffa83e Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 19 Nov 2025 17:48:01 +0300 Subject: [PATCH 13/21] Remove forge_backoff_counter --- Include/internal/pycore_backoff.h | 8 -------- Python/ceval_macros.h | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index f155236980843f..9bf779a7f87ef0 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -53,14 +53,6 @@ static _Py_BackoffCounter backoff_counter_next_table[] = { MAKE_BACKOFF_COUNTER(8190, 6), }; -static inline _Py_BackoffCounter -forge_backoff_counter(uint16_t counter) -{ - _Py_BackoffCounter result; - result.value_and_backoff = counter; - return result; -} - static inline _Py_BackoffCounter restart_backoff_counter(_Py_BackoffCounter counter) { diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 05a2760671e847..fce05c237a858a 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -315,7 +315,7 @@ GETITEM(PyObject *v, Py_ssize_t i) { * which is always an integral type. */ // Force re-specialization when tracing a side exit to get good side exits. #define ADAPTIVE_COUNTER_TRIGGERS(COUNTER) \ - backoff_counter_triggers(forge_backoff_counter((COUNTER))) + backoff_counter_triggers(MAKE_BACKOFF_COUNTER(0, (COUNTER))) #define ADVANCE_ADAPTIVE_COUNTER(COUNTER) \ do { \ From ec27072724e7e6abeb5ca8d528b7204d59840986 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 19 Nov 2025 18:19:33 +0300 Subject: [PATCH 14/21] Another try to fix Windows build --- Include/internal/pycore_backoff.h | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index 9bf779a7f87ef0..0c3c36c0feed20 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -36,21 +36,26 @@ extern "C" { #define MAX_BACKOFF 6 #define UNREACHABLE_BACKOFF 7 -#define MAKE_BACKOFF_COUNTER(value, backoff) \ - ((_Py_BackoffCounter){ .value_and_backoff = (value << BACKOFF_BITS) | backoff }) +#define MAKE_VALUE_AND_BACKOFF(value, backoff) \ + ((value << BACKOFF_BITS) | backoff) + +#define MAKE_BACKOFF_COUNTER(value, backoff) \ + ((_Py_BackoffCounter){ \ + .value_and_backoff = MAKE_VALUE_AND_BACKOFF(value, backoff) \ + }) // We only use values x and backoffs b such that // x + 1 is near to 2**(2*b+1) and x + 1 is prime. -static _Py_BackoffCounter backoff_counter_next_table[] = { - MAKE_BACKOFF_COUNTER(6, 1), - MAKE_BACKOFF_COUNTER(30, 2), - MAKE_BACKOFF_COUNTER(126, 3), - MAKE_BACKOFF_COUNTER(508, 4), - MAKE_BACKOFF_COUNTER(2052, 5), - MAKE_BACKOFF_COUNTER(8190, 6), +static uint16_t value_and_backoff_next[] = { + MAKE_VALUE_AND_BACKOFF(6, 1), + MAKE_VALUE_AND_BACKOFF(30, 2), + MAKE_VALUE_AND_BACKOFF(126, 3), + MAKE_VALUE_AND_BACKOFF(508, 4), + MAKE_VALUE_AND_BACKOFF(2052, 5), + MAKE_VALUE_AND_BACKOFF(8190, 6), // We use the same backoff counter for all backoffs >= MAX_BACKOFF. - MAKE_BACKOFF_COUNTER(8190, 6), - MAKE_BACKOFF_COUNTER(8190, 6), + MAKE_VALUE_AND_BACKOFF(8190, 6), + MAKE_VALUE_AND_BACKOFF(8190, 6), }; static inline _Py_BackoffCounter @@ -58,7 +63,9 @@ restart_backoff_counter(_Py_BackoffCounter counter) { uint16_t backoff = counter.value_and_backoff & BACKOFF_MASK; assert(backoff <= MAX_BACKOFF); - return backoff_counter_next_table[backoff]; + return ((_Py_BackoffCounter){ + .value_and_backoff = value_and_backoff_next[backoff] + }); } static inline _Py_BackoffCounter From 401b6da609c8ddd74fd0321dfee3a81e994cc78a Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 19 Nov 2025 18:24:32 +0300 Subject: [PATCH 15/21] Revert make_backoff_counter --- Include/internal/pycore_backoff.h | 22 ++++++++++++++-------- Include/internal/pycore_code.h | 4 ++-- Python/ceval_macros.h | 2 +- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index 0c3c36c0feed20..b311cc3ba3e5d3 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -35,15 +35,11 @@ extern "C" { #define BACKOFF_MASK 7 #define MAX_BACKOFF 6 #define UNREACHABLE_BACKOFF 7 +#define MAX_VALUE 0x1FFF #define MAKE_VALUE_AND_BACKOFF(value, backoff) \ ((value << BACKOFF_BITS) | backoff) -#define MAKE_BACKOFF_COUNTER(value, backoff) \ - ((_Py_BackoffCounter){ \ - .value_and_backoff = MAKE_VALUE_AND_BACKOFF(value, backoff) \ - }) - // We only use values x and backoffs b such that // x + 1 is near to 2**(2*b+1) and x + 1 is prime. static uint16_t value_and_backoff_next[] = { @@ -58,6 +54,16 @@ static uint16_t value_and_backoff_next[] = { MAKE_VALUE_AND_BACKOFF(8190, 6), }; +static inline _Py_BackoffCounter +make_backoff_counter(uint16_t value, uint16_t backoff) +{ + assert(backoff <= UNREACHABLE_BACKOFF); + assert(value <= MAX_VALUE); + return ((_Py_BackoffCounter){ + .value_and_backoff = MAKE_VALUE_AND_BACKOFF(value, backoff) + }); +} + static inline _Py_BackoffCounter restart_backoff_counter(_Py_BackoffCounter counter) { @@ -113,7 +119,7 @@ trigger_backoff_counter(void) static inline _Py_BackoffCounter initial_jump_backoff_counter(void) { - return MAKE_BACKOFF_COUNTER(JUMP_BACKWARD_INITIAL_VALUE, + return make_backoff_counter(JUMP_BACKWARD_INITIAL_VALUE, JUMP_BACKWARD_INITIAL_BACKOFF); } @@ -127,7 +133,7 @@ initial_jump_backoff_counter(void) static inline _Py_BackoffCounter initial_temperature_backoff_counter(void) { - return MAKE_BACKOFF_COUNTER(SIDE_EXIT_INITIAL_VALUE, + return make_backoff_counter(SIDE_EXIT_INITIAL_VALUE, SIDE_EXIT_INITIAL_BACKOFF); } @@ -135,7 +141,7 @@ initial_temperature_backoff_counter(void) static inline _Py_BackoffCounter initial_unreachable_backoff_counter(void) { - return MAKE_BACKOFF_COUNTER(0, UNREACHABLE_BACKOFF); + return make_backoff_counter(0, UNREACHABLE_BACKOFF); } #ifdef __cplusplus diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index b5dfd471b5d8ce..b89de83fe4391e 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -468,13 +468,13 @@ write_location_entry_start(uint8_t *ptr, int code, int length) static inline _Py_BackoffCounter adaptive_counter_warmup(void) { - return MAKE_BACKOFF_COUNTER(ADAPTIVE_WARMUP_VALUE, + return make_backoff_counter(ADAPTIVE_WARMUP_VALUE, ADAPTIVE_WARMUP_BACKOFF); } static inline _Py_BackoffCounter adaptive_counter_cooldown(void) { - return MAKE_BACKOFF_COUNTER(ADAPTIVE_COOLDOWN_VALUE, + return make_backoff_counter(ADAPTIVE_COOLDOWN_VALUE, ADAPTIVE_COOLDOWN_BACKOFF); } diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index fce05c237a858a..14550d2307e2f5 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -315,7 +315,7 @@ GETITEM(PyObject *v, Py_ssize_t i) { * which is always an integral type. */ // Force re-specialization when tracing a side exit to get good side exits. #define ADAPTIVE_COUNTER_TRIGGERS(COUNTER) \ - backoff_counter_triggers(MAKE_BACKOFF_COUNTER(0, (COUNTER))) + backoff_counter_triggers(make_backoff_counter(0, (COUNTER))) #define ADVANCE_ADAPTIVE_COUNTER(COUNTER) \ do { \ From 3418be123fa1bb836bed00c2324845d02de254c7 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 19 Nov 2025 18:28:42 +0300 Subject: [PATCH 16/21] Revert changes to pycore_code.h --- Include/internal/pycore_code.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index b89de83fe4391e..cb9c0aa27a1785 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -466,16 +466,21 @@ write_location_entry_start(uint8_t *ptr, int code, int length) # error "Cold exit value should be larger than adaptive cooldown value" #endif +static inline _Py_BackoffCounter +adaptive_counter_bits(uint16_t value, uint16_t backoff) { + return make_backoff_counter(value, backoff); +} + static inline _Py_BackoffCounter adaptive_counter_warmup(void) { - return make_backoff_counter(ADAPTIVE_WARMUP_VALUE, - ADAPTIVE_WARMUP_BACKOFF); + return adaptive_counter_bits(ADAPTIVE_WARMUP_VALUE, + ADAPTIVE_WARMUP_BACKOFF); } static inline _Py_BackoffCounter adaptive_counter_cooldown(void) { - return make_backoff_counter(ADAPTIVE_COOLDOWN_VALUE, - ADAPTIVE_COOLDOWN_BACKOFF); + return adaptive_counter_bits(ADAPTIVE_COOLDOWN_VALUE, + ADAPTIVE_COOLDOWN_BACKOFF); } static inline _Py_BackoffCounter From ceddb6a96714a02d3ae63b3ddd7ddffe56a08f21 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 19 Nov 2025 18:30:39 +0300 Subject: [PATCH 17/21] Comment fix --- Include/internal/pycore_backoff.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index b311cc3ba3e5d3..873447bced6a25 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -25,7 +25,8 @@ extern "C" { The 16-bit counter is structured as a 13-bit unsigned 'value' and a 3-bit 'backoff' field. When resetting the counter, the backoff field is incremented (until it reaches a limit) and the - value is set to a bit mask representing the value 2**(2*backoff+1) - 1. + value is set to a bit mask representing some prime value near + to 2**(2*backoff+1) - 1, see value_and_backoff_next. The maximum backoff is 6, since 7 is an UNREACHABLE_BACKOFF. There is an exceptional value which must not be updated, 0xFFFF. From a89d6decfbbe4aeed4a49e14b930acf48b0cbc44 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 19 Nov 2025 18:47:54 +0300 Subject: [PATCH 18/21] Temporarily disable assert --- Include/internal/pycore_backoff.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index 873447bced6a25..d4cfc70cacf107 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -58,7 +58,7 @@ static uint16_t value_and_backoff_next[] = { static inline _Py_BackoffCounter make_backoff_counter(uint16_t value, uint16_t backoff) { - assert(backoff <= UNREACHABLE_BACKOFF); + //assert(backoff <= UNREACHABLE_BACKOFF); assert(value <= MAX_VALUE); return ((_Py_BackoffCounter){ .value_and_backoff = MAKE_VALUE_AND_BACKOFF(value, backoff) From c7873c276668e56f4d8d94474e4eb767bff37e35 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 19 Nov 2025 18:58:18 +0300 Subject: [PATCH 19/21] Revert forge_backoff_counter --- Include/internal/pycore_backoff.h | 10 +++++++++- Python/ceval_macros.h | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index d4cfc70cacf107..2391ecd760903a 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -58,13 +58,21 @@ static uint16_t value_and_backoff_next[] = { static inline _Py_BackoffCounter make_backoff_counter(uint16_t value, uint16_t backoff) { - //assert(backoff <= UNREACHABLE_BACKOFF); + assert(backoff <= UNREACHABLE_BACKOFF); assert(value <= MAX_VALUE); return ((_Py_BackoffCounter){ .value_and_backoff = MAKE_VALUE_AND_BACKOFF(value, backoff) }); } +static inline _Py_BackoffCounter +forge_backoff_counter(uint16_t counter) +{ + _Py_BackoffCounter result; + result.value_and_backoff = counter; + return result; +} + static inline _Py_BackoffCounter restart_backoff_counter(_Py_BackoffCounter counter) { diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 14550d2307e2f5..05a2760671e847 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -315,7 +315,7 @@ GETITEM(PyObject *v, Py_ssize_t i) { * which is always an integral type. */ // Force re-specialization when tracing a side exit to get good side exits. #define ADAPTIVE_COUNTER_TRIGGERS(COUNTER) \ - backoff_counter_triggers(make_backoff_counter(0, (COUNTER))) + backoff_counter_triggers(forge_backoff_counter((COUNTER))) #define ADVANCE_ADAPTIVE_COUNTER(COUNTER) \ do { \ From 5b7bfd9db3054669155efbeb0cade572b62982cf Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Wed, 19 Nov 2025 20:09:09 +0300 Subject: [PATCH 20/21] Fix CI, NEWS updated --- Include/internal/pycore_backoff.h | 2 +- .../2025-11-15-14-04-35.gh-issue-141589.VfdMDD.rst | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index 2391ecd760903a..c9b016cad18b39 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -43,7 +43,7 @@ extern "C" { // We only use values x and backoffs b such that // x + 1 is near to 2**(2*b+1) and x + 1 is prime. -static uint16_t value_and_backoff_next[] = { +static const uint16_t value_and_backoff_next[] = { MAKE_VALUE_AND_BACKOFF(6, 1), MAKE_VALUE_AND_BACKOFF(30, 2), MAKE_VALUE_AND_BACKOFF(126, 3), diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-15-14-04-35.gh-issue-141589.VfdMDD.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-15-14-04-35.gh-issue-141589.VfdMDD.rst index 10c8d268e42994..5eb0e0c8b89f7a 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-15-14-04-35.gh-issue-141589.VfdMDD.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-15-14-04-35.gh-issue-141589.VfdMDD.rst @@ -1,2 +1,3 @@ -Change ``backoff counter`` to use powers of 4 instead of powers of 2. +Change ``backoff counter`` to use prime numbers instead of powers of 2. +Use only 3 bits for ``counter`` and 13 bits for ``value``. This allows to support values up to 8191. Patch by Mikhail Efimov. From dda4cdb215e7e0ec7eb340d72cf3b4445db8f900 Mon Sep 17 00:00:00 2001 From: Mikhail Efimov Date: Thu, 20 Nov 2025 18:41:42 +0300 Subject: [PATCH 21/21] Review addressed: change values and comment --- Include/internal/pycore_backoff.h | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h index c9b016cad18b39..7f60eb495080ae 100644 --- a/Include/internal/pycore_backoff.h +++ b/Include/internal/pycore_backoff.h @@ -25,8 +25,9 @@ extern "C" { The 16-bit counter is structured as a 13-bit unsigned 'value' and a 3-bit 'backoff' field. When resetting the counter, the backoff field is incremented (until it reaches a limit) and the - value is set to a bit mask representing some prime value near - to 2**(2*backoff+1) - 1, see value_and_backoff_next. + value is set to a bit mask representing some prime value - 1. + New values and backoffs for each backoff are calculated once + at compile time and saved to value_and_backoff_next table. The maximum backoff is 6, since 7 is an UNREACHABLE_BACKOFF. There is an exceptional value which must not be updated, 0xFFFF. @@ -41,15 +42,15 @@ extern "C" { #define MAKE_VALUE_AND_BACKOFF(value, backoff) \ ((value << BACKOFF_BITS) | backoff) -// We only use values x and backoffs b such that +// For previous backoff b we use value x such that // x + 1 is near to 2**(2*b+1) and x + 1 is prime. static const uint16_t value_and_backoff_next[] = { - MAKE_VALUE_AND_BACKOFF(6, 1), - MAKE_VALUE_AND_BACKOFF(30, 2), - MAKE_VALUE_AND_BACKOFF(126, 3), - MAKE_VALUE_AND_BACKOFF(508, 4), - MAKE_VALUE_AND_BACKOFF(2052, 5), - MAKE_VALUE_AND_BACKOFF(8190, 6), + MAKE_VALUE_AND_BACKOFF(1, 1), + MAKE_VALUE_AND_BACKOFF(6, 2), + MAKE_VALUE_AND_BACKOFF(30, 3), + MAKE_VALUE_AND_BACKOFF(126, 4), + MAKE_VALUE_AND_BACKOFF(508, 5), + MAKE_VALUE_AND_BACKOFF(2052, 6), // We use the same backoff counter for all backoffs >= MAX_BACKOFF. MAKE_VALUE_AND_BACKOFF(8190, 6), MAKE_VALUE_AND_BACKOFF(8190, 6),