2024-04-04 12:03:27 -03:00
|
|
|
|
|
|
|
#ifndef Py_INTERNAL_BACKOFF_H
|
|
|
|
#define Py_INTERNAL_BACKOFF_H
|
|
|
|
#ifdef __cplusplus
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef Py_BUILD_CORE
|
|
|
|
# error "this header requires Py_BUILD_CORE define"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
|
2024-06-26 08:54:03 -03:00
|
|
|
|
|
|
|
typedef struct {
|
2024-10-07 07:46:33 -03:00
|
|
|
uint16_t value_and_backoff;
|
2024-06-26 08:54:03 -03:00
|
|
|
} _Py_BackoffCounter;
|
|
|
|
|
|
|
|
|
2024-04-04 12:03:27 -03:00
|
|
|
/* 16-bit countdown counters using exponential backoff.
|
|
|
|
|
|
|
|
These are used by the adaptive specializer to count down until
|
|
|
|
it is time to specialize an instruction. If specialization fails
|
|
|
|
the counter is reset using exponential backoff.
|
|
|
|
|
|
|
|
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
|
|
|
|
backoff field is incremented (until it reaches a limit) and the
|
|
|
|
value is set to a bit mask representing the value 2**backoff - 1.
|
2024-10-07 07:46:33 -03:00
|
|
|
The maximum backoff is 12 (the number of bits in the value).
|
2024-04-04 12:03:27 -03:00
|
|
|
|
|
|
|
There is an exceptional value which must not be updated, 0xFFFF.
|
|
|
|
*/
|
|
|
|
|
2024-10-07 07:46:33 -03:00
|
|
|
#define BACKOFF_BITS 4
|
|
|
|
#define MAX_BACKOFF 12
|
|
|
|
#define UNREACHABLE_BACKOFF 15
|
2024-04-04 12:03:27 -03:00
|
|
|
|
|
|
|
static inline bool
|
|
|
|
is_unreachable_backoff_counter(_Py_BackoffCounter counter)
|
|
|
|
{
|
2024-10-07 07:46:33 -03:00
|
|
|
return counter.value_and_backoff == UNREACHABLE_BACKOFF;
|
2024-04-04 12:03:27 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline _Py_BackoffCounter
|
|
|
|
make_backoff_counter(uint16_t value, uint16_t backoff)
|
|
|
|
{
|
|
|
|
assert(backoff <= 15);
|
|
|
|
assert(value <= 0xFFF);
|
2024-05-05 16:28:55 -03:00
|
|
|
_Py_BackoffCounter result;
|
2024-10-07 07:46:33 -03:00
|
|
|
result.value_and_backoff = (value << BACKOFF_BITS) | backoff;
|
2024-05-05 16:28:55 -03:00
|
|
|
return result;
|
2024-04-04 12:03:27 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline _Py_BackoffCounter
|
|
|
|
forge_backoff_counter(uint16_t counter)
|
|
|
|
{
|
2024-05-05 16:28:55 -03:00
|
|
|
_Py_BackoffCounter result;
|
2024-10-07 07:46:33 -03:00
|
|
|
result.value_and_backoff = counter;
|
2024-05-05 16:28:55 -03:00
|
|
|
return result;
|
2024-04-04 12:03:27 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline _Py_BackoffCounter
|
|
|
|
restart_backoff_counter(_Py_BackoffCounter counter)
|
|
|
|
{
|
|
|
|
assert(!is_unreachable_backoff_counter(counter));
|
2024-10-07 07:46:33 -03:00
|
|
|
int backoff = counter.value_and_backoff & 15;
|
|
|
|
if (backoff < MAX_BACKOFF) {
|
|
|
|
return make_backoff_counter((1 << (backoff + 1)) - 1, backoff + 1);
|
2024-04-04 12:03:27 -03:00
|
|
|
}
|
|
|
|
else {
|
2024-10-07 07:46:33 -03:00
|
|
|
return make_backoff_counter((1 << MAX_BACKOFF) - 1, MAX_BACKOFF);
|
2024-04-04 12:03:27 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline _Py_BackoffCounter
|
|
|
|
pause_backoff_counter(_Py_BackoffCounter counter)
|
|
|
|
{
|
2024-10-07 07:46:33 -03:00
|
|
|
_Py_BackoffCounter result;
|
|
|
|
result.value_and_backoff = counter.value_and_backoff | (1 << BACKOFF_BITS);
|
|
|
|
return result;
|
2024-04-04 12:03:27 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline _Py_BackoffCounter
|
|
|
|
advance_backoff_counter(_Py_BackoffCounter counter)
|
|
|
|
{
|
2024-10-07 07:46:33 -03:00
|
|
|
_Py_BackoffCounter result;
|
|
|
|
result.value_and_backoff = counter.value_and_backoff - (1 << BACKOFF_BITS);
|
|
|
|
return result;
|
2024-04-04 12:03:27 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
backoff_counter_triggers(_Py_BackoffCounter counter)
|
|
|
|
{
|
2024-10-07 07:46:33 -03:00
|
|
|
/* Test whether the value is zero and the backoff is not UNREACHABLE_BACKOFF */
|
|
|
|
return counter.value_and_backoff < UNREACHABLE_BACKOFF;
|
2024-04-04 12:03:27 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Initial JUMP_BACKWARD counter.
|
|
|
|
* This determines when we create a trace for a loop.
|
|
|
|
* Backoff sequence 16, 32, 64, 128, 256, 512, 1024, 2048, 4096. */
|
2024-09-27 13:38:04 -03:00
|
|
|
#define JUMP_BACKWARD_INITIAL_VALUE 15
|
2024-04-04 12:03:27 -03:00
|
|
|
#define JUMP_BACKWARD_INITIAL_BACKOFF 4
|
|
|
|
static inline _Py_BackoffCounter
|
|
|
|
initial_jump_backoff_counter(void)
|
|
|
|
{
|
|
|
|
return make_backoff_counter(JUMP_BACKWARD_INITIAL_VALUE,
|
|
|
|
JUMP_BACKWARD_INITIAL_BACKOFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initial exit temperature.
|
|
|
|
* Must be larger than ADAPTIVE_COOLDOWN_VALUE,
|
|
|
|
* otherwise when a side exit warms up we may construct
|
|
|
|
* a new trace before the Tier 1 code has properly re-specialized.
|
|
|
|
* Backoff sequence 64, 128, 256, 512, 1024, 2048, 4096. */
|
2024-09-27 13:38:04 -03:00
|
|
|
#define SIDE_EXIT_INITIAL_VALUE 63
|
2024-07-01 17:17:40 -03:00
|
|
|
#define SIDE_EXIT_INITIAL_BACKOFF 6
|
2024-04-04 12:03:27 -03:00
|
|
|
|
|
|
|
static inline _Py_BackoffCounter
|
|
|
|
initial_temperature_backoff_counter(void)
|
|
|
|
{
|
2024-07-01 17:17:40 -03:00
|
|
|
return make_backoff_counter(SIDE_EXIT_INITIAL_VALUE,
|
|
|
|
SIDE_EXIT_INITIAL_BACKOFF);
|
2024-04-04 12:03:27 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Unreachable backoff counter. */
|
|
|
|
static inline _Py_BackoffCounter
|
|
|
|
initial_unreachable_backoff_counter(void)
|
|
|
|
{
|
2024-10-07 07:46:33 -03:00
|
|
|
return make_backoff_counter(0, UNREACHABLE_BACKOFF);
|
2024-04-04 12:03:27 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif /* !Py_INTERNAL_BACKOFF_H */
|