mirror of
https://github.com/php/php-src.git
synced 2025-08-15 13:38:49 +02:00
Fix GH-13193: Significant performance degradation in 'foreach' starting from PHP 8.2.13 (caused by garbage collection) (#13265)
* Fix GH-13193: Significant performance degradation in 'foreach' starting from PHP 8.2.13 (caused by garbage collection) * Don't run zend_gc_remove_root_tmpvars() if GC is not active or GC buffer is empty
This commit is contained in:
parent
f26dd1354b
commit
397d4c244d
1 changed files with 38 additions and 3 deletions
|
@ -1465,7 +1465,8 @@ next:
|
|||
}
|
||||
|
||||
static void zend_get_gc_buffer_release(void);
|
||||
static void zend_gc_root_tmpvars(void);
|
||||
static void zend_gc_check_root_tmpvars(void);
|
||||
static void zend_gc_remove_root_tmpvars(void);
|
||||
|
||||
ZEND_API int zend_gc_collect_cycles(void)
|
||||
{
|
||||
|
@ -1473,6 +1474,10 @@ ZEND_API int zend_gc_collect_cycles(void)
|
|||
bool should_rerun_gc = 0;
|
||||
bool did_rerun_gc = 0;
|
||||
|
||||
if (GC_G(num_roots) && GC_G(gc_active)) {
|
||||
zend_gc_remove_root_tmpvars();
|
||||
}
|
||||
|
||||
rerun_gc:
|
||||
if (GC_G(num_roots)) {
|
||||
int count;
|
||||
|
@ -1669,7 +1674,7 @@ rerun_gc:
|
|||
|
||||
finish:
|
||||
zend_get_gc_buffer_release();
|
||||
zend_gc_root_tmpvars();
|
||||
zend_gc_check_root_tmpvars();
|
||||
return total_count;
|
||||
}
|
||||
|
||||
|
@ -1707,7 +1712,7 @@ static void zend_get_gc_buffer_release(void) {
|
|||
* cycles. However, there are some rare exceptions where this is possible, in which case we rely
|
||||
* on the producing code to root the value. If a GC run occurs between the rooting and consumption
|
||||
* of the value, we would end up leaking it. To avoid this, root all live TMPVAR values here. */
|
||||
static void zend_gc_root_tmpvars(void) {
|
||||
static void zend_gc_check_root_tmpvars(void) {
|
||||
zend_execute_data *ex = EG(current_execute_data);
|
||||
for (; ex; ex = ex->prev_execute_data) {
|
||||
zend_function *func = ex->func;
|
||||
|
@ -1737,6 +1742,36 @@ static void zend_gc_root_tmpvars(void) {
|
|||
}
|
||||
}
|
||||
|
||||
static void zend_gc_remove_root_tmpvars(void) {
|
||||
zend_execute_data *ex = EG(current_execute_data);
|
||||
for (; ex; ex = ex->prev_execute_data) {
|
||||
zend_function *func = ex->func;
|
||||
if (!func || !ZEND_USER_CODE(func->type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t op_num = ex->opline - ex->func->op_array.opcodes;
|
||||
for (uint32_t i = 0; i < func->op_array.last_live_range; i++) {
|
||||
const zend_live_range *range = &func->op_array.live_range[i];
|
||||
if (range->start > op_num) {
|
||||
break;
|
||||
}
|
||||
if (range->end <= op_num) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t kind = range->var & ZEND_LIVE_MASK;
|
||||
if (kind == ZEND_LIVE_TMPVAR || kind == ZEND_LIVE_LOOP) {
|
||||
uint32_t var_num = range->var & ~ZEND_LIVE_MASK;
|
||||
zval *var = ZEND_CALL_VAR(ex, var_num);
|
||||
if (Z_REFCOUNTED_P(var)) {
|
||||
GC_REMOVE_FROM_BUFFER(Z_COUNTED_P(var));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ZTS
|
||||
size_t zend_gc_globals_size(void)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue