Skip to content

Commit 373237e

Browse files
committed
Merge branch 'PHP-8.3'
* PHP-8.3: Fix GH-13193: Significant performance degradation in 'foreach' starting from PHP 8.2.13 (caused by garbage collection) (#13265)
2 parents 34e2dc5 + 49f85c2 commit 373237e

File tree

1 file changed

+37
-3
lines changed

1 file changed

+37
-3
lines changed

Zend/zend_gc.c

+37-3
Original file line numberDiff line numberDiff line change
@@ -1773,7 +1773,8 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe
17731773
}
17741774

17751775
static void zend_get_gc_buffer_release(void);
1776-
static void zend_gc_root_tmpvars(void);
1776+
static void zend_gc_check_root_tmpvars(void);
1777+
static void zend_gc_remove_root_tmpvars(void);
17771778

17781779
ZEND_API int zend_gc_collect_cycles(void)
17791780
{
@@ -1782,6 +1783,9 @@ ZEND_API int zend_gc_collect_cycles(void)
17821783
bool did_rerun_gc = 0;
17831784

17841785
zend_hrtime_t start_time = zend_hrtime();
1786+
if (GC_G(num_roots) && GC_G(gc_active)) {
1787+
zend_gc_remove_root_tmpvars();
1788+
}
17851789

17861790
rerun_gc:
17871791
if (GC_G(num_roots)) {
@@ -1986,7 +1990,7 @@ ZEND_API int zend_gc_collect_cycles(void)
19861990

19871991
finish:
19881992
zend_get_gc_buffer_release();
1989-
zend_gc_root_tmpvars();
1993+
zend_gc_check_root_tmpvars();
19901994
GC_G(collector_time) += zend_hrtime() - start_time;
19911995
return total_count;
19921996
}
@@ -2033,7 +2037,7 @@ static void zend_get_gc_buffer_release(void) {
20332037
* cycles. However, there are some rare exceptions where this is possible, in which case we rely
20342038
* on the producing code to root the value. If a GC run occurs between the rooting and consumption
20352039
* of the value, we would end up leaking it. To avoid this, root all live TMPVAR values here. */
2036-
static void zend_gc_root_tmpvars(void) {
2040+
static void zend_gc_check_root_tmpvars(void) {
20372041
zend_execute_data *ex = EG(current_execute_data);
20382042
for (; ex; ex = ex->prev_execute_data) {
20392043
zend_function *func = ex->func;
@@ -2063,6 +2067,36 @@ static void zend_gc_root_tmpvars(void) {
20632067
}
20642068
}
20652069

2070+
static void zend_gc_remove_root_tmpvars(void) {
2071+
zend_execute_data *ex = EG(current_execute_data);
2072+
for (; ex; ex = ex->prev_execute_data) {
2073+
zend_function *func = ex->func;
2074+
if (!func || !ZEND_USER_CODE(func->type)) {
2075+
continue;
2076+
}
2077+
2078+
uint32_t op_num = ex->opline - ex->func->op_array.opcodes;
2079+
for (uint32_t i = 0; i < func->op_array.last_live_range; i++) {
2080+
const zend_live_range *range = &func->op_array.live_range[i];
2081+
if (range->start > op_num) {
2082+
break;
2083+
}
2084+
if (range->end <= op_num) {
2085+
continue;
2086+
}
2087+
2088+
uint32_t kind = range->var & ZEND_LIVE_MASK;
2089+
if (kind == ZEND_LIVE_TMPVAR || kind == ZEND_LIVE_LOOP) {
2090+
uint32_t var_num = range->var & ~ZEND_LIVE_MASK;
2091+
zval *var = ZEND_CALL_VAR(ex, var_num);
2092+
if (Z_REFCOUNTED_P(var)) {
2093+
GC_REMOVE_FROM_BUFFER(Z_COUNTED_P(var));
2094+
}
2095+
}
2096+
}
2097+
}
2098+
}
2099+
20662100
#if GC_BENCH
20672101
void gc_bench_print(void)
20682102
{

0 commit comments

Comments
 (0)