Merge branch 'PHP-8.2' into PHP-8.3

* PHP-8.2:
  Fix GH-13193: Significant performance degradation in 'foreach' starting from PHP 8.2.13 (caused by garbage collection) (#13265)
This commit is contained in:
Dmitry Stogov 2024-01-30 06:40:44 +03:00
commit 49f85c24da

View file

@ -1773,7 +1773,8 @@ next:
} }
static void zend_get_gc_buffer_release(void); 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) ZEND_API int zend_gc_collect_cycles(void)
{ {
@ -1782,6 +1783,9 @@ ZEND_API int zend_gc_collect_cycles(void)
bool did_rerun_gc = 0; bool did_rerun_gc = 0;
zend_hrtime_t start_time = zend_hrtime(); zend_hrtime_t start_time = zend_hrtime();
if (GC_G(num_roots) && GC_G(gc_active)) {
zend_gc_remove_root_tmpvars();
}
rerun_gc: rerun_gc:
if (GC_G(num_roots)) { if (GC_G(num_roots)) {
@ -1986,7 +1990,7 @@ rerun_gc:
finish: finish:
zend_get_gc_buffer_release(); zend_get_gc_buffer_release();
zend_gc_root_tmpvars(); zend_gc_check_root_tmpvars();
GC_G(collector_time) += zend_hrtime() - start_time; GC_G(collector_time) += zend_hrtime() - start_time;
return total_count; return total_count;
} }
@ -2033,7 +2037,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 * 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 * 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. */ * 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); zend_execute_data *ex = EG(current_execute_data);
for (; ex; ex = ex->prev_execute_data) { for (; ex; ex = ex->prev_execute_data) {
zend_function *func = ex->func; zend_function *func = ex->func;
@ -2063,6 +2067,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));
}
}
}
}
}
#if GC_BENCH #if GC_BENCH
void gc_bench_print(void) void gc_bench_print(void)
{ {