1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Reduce gc stack usage for strings (and resources)

Adding strings to the worklist is useless, because they never contribute to
cycles. The assembly size on x86_64 does not change. This significantly improves
performance in this synthetic benchmark by 33%.

    function test($a) {}

    $a = new stdClass();
    $a->self = $a;
    $a->prop1 = str_repeat('a', 10);
    $a->prop2 = str_repeat('a', 10);
    $a->prop3 = str_repeat('a', 10);
    $a->prop4 = str_repeat('a', 10);
    $a->prop5 = str_repeat('a', 10);
    $a->prop6 = str_repeat('a', 10);
    $a->prop7 = str_repeat('a', 10);
    $a->prop8 = str_repeat('a', 10);
    $a->prop9 = str_repeat('a', 10);
    $a->prop10 = str_repeat('a', 10);

    for ($i = 0; $i < 10_000_000; $i++) {
        test($a);
        gc_collect_cycles();
    }

This requires adding IS_TYPE_COLLECTABLE to IS_REFERENCE_EX to ensure these
values continue to be pushed onto the stack. Luckily, IS_TYPE_COLLECTABLE is
currently only used in gc_check_possible_root(), where the checked value cannot
be a reference.

Note that this changes the output of gc_collect_cycles(). Non-cyclic, refcounted
values no longer count towards the total reported values collected.

Also, there is some obvious overlap with GH-17130. This change should be good
nonetheless, especially if we can remove the GC_COLLECTABLE(Z_COUNTED_P(zv))
condition in PHP 9 and rely on Z_COLLECTABLE_P() exclusively, given we can
assume an object doesn't become cyclic at runtime anymore.

Closes GH-17194
This commit is contained in:
Ilija Tovilo
2024-12-17 15:17:52 +01:00
parent 287aeebba7
commit e69317b501
3 changed files with 46 additions and 41 deletions

View File

@@ -34,6 +34,8 @@ PHP 8.5 UPGRADE NOTES
$object. If compared to a statically unknown value $object == $true, it
would always return false. This behavior was consolidated to always follow
the behavior of (bool) $object.
. The return value of gc_collect_cycles() no longer includes strings and
resources that were indirectly collected through cycles.
- Intl:
. The extension now requires at least ICU 57.1.

View File

@@ -818,7 +818,7 @@ tail_call:
zval *entry = (zval*) Z_PTR_P(zv);
zval *weakmap = zv+1;
ZEND_ASSERT(Z_REFCOUNTED_P(weakmap));
if (Z_OPT_REFCOUNTED_P(entry)) {
if (Z_OPT_COLLECTABLE_P(entry)) {
GC_UNSET_FROM_WEAKMAP_KEY(entry);
if (GC_REF_CHECK_COLOR(Z_COUNTED_P(weakmap), GC_GREY)) {
/* Weakmap was scanned in gc_mark_roots, we must
@@ -855,7 +855,7 @@ tail_call:
ZEND_ASSERT(Z_TYPE_P(zv+1) == IS_PTR);
zval *key = zv;
zval *entry = (zval*) Z_PTR_P(zv+1);
if (Z_OPT_REFCOUNTED_P(entry)) {
if (Z_OPT_COLLECTABLE_P(entry)) {
GC_UNSET_FROM_WEAKMAP(entry);
if (GC_REF_CHECK_COLOR(Z_COUNTED_P(key), GC_GREY)) {
/* Key was scanned in gc_mark_roots, we must
@@ -893,7 +893,7 @@ tail_call:
if (!GC_REF_CHECK_COLOR(ht, GC_BLACK)) {
GC_REF_SET_BLACK(ht);
for (; n != 0; n--) {
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) {
@@ -909,14 +909,14 @@ tail_call:
handle_zvals:
for (; n != 0; n--) {
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) {
GC_REF_SET_BLACK(ref);
zv++;
while (--n) {
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) {
@@ -948,7 +948,7 @@ handle_ht:
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) {
@@ -959,7 +959,7 @@ handle_ht:
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) {
@@ -975,7 +975,7 @@ handle_ht:
p++;
}
} else if (GC_TYPE(ref) == IS_REFERENCE) {
if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
if (Z_COLLECTABLE(((zend_reference*)ref)->val)) {
ref = Z_COUNTED(((zend_reference*)ref)->val);
GC_ADDREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_BLACK)) {
@@ -1019,7 +1019,7 @@ tail_call:
zval *entry = (zval*) Z_PTR_P(zv);
zval *weakmap = zv+1;
ZEND_ASSERT(Z_REFCOUNTED_P(weakmap));
if (Z_REFCOUNTED_P(entry)) {
if (Z_COLLECTABLE_P(entry)) {
GC_SET_FROM_WEAKMAP_KEY(entry);
ref = Z_COUNTED_P(entry);
/* Only DELREF if the contribution from the weakmap has
@@ -1043,7 +1043,7 @@ tail_call:
for (; n != 0; n--) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
if (Z_REFCOUNTED_P(entry)) {
if (Z_COLLECTABLE_P(entry)) {
GC_SET_FROM_WEAKMAP(entry);
ref = Z_COUNTED_P(entry);
/* Only DELREF if the contribution from the weakmap key
@@ -1069,7 +1069,7 @@ tail_call:
if (!GC_REF_CHECK_COLOR(ht, GC_GREY)) {
GC_REF_SET_COLOR(ht, GC_GREY);
for (; n != 0; n--) {
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_DELREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) {
@@ -1084,14 +1084,14 @@ tail_call:
}
handle_zvals:
for (; n != 0; n--) {
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_DELREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_GREY);
zv++;
while (--n) {
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
GC_DELREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) {
@@ -1123,7 +1123,7 @@ handle_ht:
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_DELREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) {
@@ -1134,7 +1134,7 @@ handle_ht:
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
GC_DELREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) {
@@ -1150,7 +1150,7 @@ handle_ht:
p++;
}
} else if (GC_TYPE(ref) == IS_REFERENCE) {
if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
if (Z_COLLECTABLE(((zend_reference*)ref)->val)) {
ref = Z_COUNTED(((zend_reference*)ref)->val);
GC_DELREF(ref);
if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) {
@@ -1263,7 +1263,7 @@ tail_call:
for (; n != 0; n--) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
if (Z_OPT_REFCOUNTED_P(entry)) {
if (Z_OPT_COLLECTABLE_P(entry)) {
ref = Z_COUNTED_P(entry);
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_WHITE);
@@ -1282,7 +1282,7 @@ tail_call:
GC_REF_SET_COLOR(ht, GC_WHITE);
GC_STACK_PUSH((zend_refcounted *) ht);
for (; n != 0; n--) {
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_WHITE);
@@ -1297,13 +1297,13 @@ tail_call:
handle_zvals:
for (; n != 0; n--) {
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_WHITE);
zv++;
while (--n) {
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_WHITE);
@@ -1335,7 +1335,7 @@ handle_ht:
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_WHITE);
@@ -1345,7 +1345,7 @@ handle_ht:
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_WHITE);
@@ -1360,7 +1360,7 @@ handle_ht:
p++;
}
} else if (GC_TYPE(ref) == IS_REFERENCE) {
if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
if (Z_COLLECTABLE(((zend_reference*)ref)->val)) {
ref = Z_COUNTED(((zend_reference*)ref)->val);
if (GC_REF_CHECK_COLOR(ref, GC_GREY)) {
GC_REF_SET_COLOR(ref, GC_WHITE);
@@ -1473,7 +1473,7 @@ tail_call:
for (; n != 0; n--) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
if (Z_REFCOUNTED_P(entry) && GC_FROM_WEAKMAP_KEY(entry)) {
if (Z_COLLECTABLE_P(entry) && GC_FROM_WEAKMAP_KEY(entry)) {
GC_UNSET_FROM_WEAKMAP_KEY(entry);
GC_UNSET_FROM_WEAKMAP(entry);
ref = Z_COUNTED_P(entry);
@@ -1494,7 +1494,7 @@ tail_call:
for (; n != 0; n--) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
if (Z_REFCOUNTED_P(entry) && GC_FROM_WEAKMAP(entry)) {
if (Z_COLLECTABLE_P(entry) && GC_FROM_WEAKMAP(entry)) {
GC_UNSET_FROM_WEAKMAP_KEY(entry);
GC_UNSET_FROM_WEAKMAP(entry);
ref = Z_COUNTED_P(entry);
@@ -1517,7 +1517,7 @@ tail_call:
if (GC_REF_CHECK_COLOR(ht, GC_WHITE)) {
GC_REF_SET_BLACK(ht);
for (; n != 0; n--) {
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) {
@@ -1533,14 +1533,14 @@ tail_call:
handle_zvals:
for (; n != 0; n--) {
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) {
GC_REF_SET_BLACK(ref);
zv++;
while (--n) {
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) {
@@ -1576,7 +1576,7 @@ handle_ht:
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) {
@@ -1587,7 +1587,7 @@ handle_ht:
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
GC_ADDREF(ref);
if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) {
@@ -1603,7 +1603,7 @@ handle_ht:
p++;
}
} else if (GC_TYPE(ref) == IS_REFERENCE) {
if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
if (Z_COLLECTABLE(((zend_reference*)ref)->val)) {
ref = Z_COUNTED(((zend_reference*)ref)->val);
GC_ADDREF(ref);
if (GC_REF_CHECK_COLOR(ref, GC_WHITE)) {
@@ -1681,7 +1681,7 @@ tail_call:
GC_REMOVE_FROM_BUFFER(ref);
count++;
} else if (GC_TYPE(ref) == IS_REFERENCE) {
if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
if (Z_COLLECTABLE(((zend_reference*)ref)->val)) {
ref = Z_COUNTED(((zend_reference*)ref)->val);
goto tail_call;
}
@@ -1704,7 +1704,7 @@ tail_call:
for (; n != 0; n--) {
ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR);
zval *entry = (zval*) Z_PTR_P(zv);
if (Z_OPT_REFCOUNTED_P(entry)) {
if (Z_OPT_COLLECTABLE_P(entry)) {
ref = Z_COUNTED_P(entry);
GC_STACK_PUSH(ref);
}
@@ -1717,7 +1717,7 @@ tail_call:
zv = table;
if (UNEXPECTED(ht)) {
for (; n != 0; n--) {
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_STACK_PUSH(ref);
}
@@ -1732,11 +1732,11 @@ tail_call:
handle_zvals:
for (; n != 0; n--) {
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
zv++;
while (--n) {
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
GC_STACK_PUSH(ref);
}
@@ -1763,7 +1763,7 @@ handle_ht:
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
ref = Z_COUNTED_P(zv);
p++;
while (--n) {
@@ -1771,7 +1771,7 @@ handle_ht:
if (Z_TYPE_P(zv) == IS_INDIRECT) {
zv = Z_INDIRECT_P(zv);
}
if (Z_REFCOUNTED_P(zv)) {
if (Z_COLLECTABLE_P(zv)) {
zend_refcounted *ref = Z_COUNTED_P(zv);
GC_STACK_PUSH(ref);
}
@@ -2175,7 +2175,7 @@ static void zend_gc_check_root_tmpvars(void) {
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)) {
if (Z_COLLECTABLE_P(var)) {
gc_check_possible_root(Z_COUNTED_P(var));
}
}
@@ -2205,7 +2205,7 @@ static void zend_gc_remove_root_tmpvars(void) {
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)) {
if (Z_COLLECTABLE_P(var)) {
GC_REMOVE_FROM_BUFFER(Z_COUNTED_P(var));
}
}

View File

@@ -808,7 +808,7 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) {
#define IS_ARRAY_EX (IS_ARRAY | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT) | (IS_TYPE_COLLECTABLE << Z_TYPE_FLAGS_SHIFT))
#define IS_OBJECT_EX (IS_OBJECT | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT) | (IS_TYPE_COLLECTABLE << Z_TYPE_FLAGS_SHIFT))
#define IS_RESOURCE_EX (IS_RESOURCE | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT))
#define IS_REFERENCE_EX (IS_REFERENCE | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT))
#define IS_REFERENCE_EX (IS_REFERENCE | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT) | (IS_TYPE_COLLECTABLE << Z_TYPE_FLAGS_SHIFT))
#define IS_CONSTANT_AST_EX (IS_CONSTANT_AST | (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT))
@@ -943,6 +943,9 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) {
#define Z_OPT_REFCOUNTED(zval) Z_TYPE_INFO_REFCOUNTED(Z_TYPE_INFO(zval))
#define Z_OPT_REFCOUNTED_P(zval_p) Z_OPT_REFCOUNTED(*(zval_p))
#define Z_OPT_COLLECTABLE(zval) ((Z_TYPE_INFO(zval) & (IS_TYPE_COLLECTABLE << Z_TYPE_FLAGS_SHIFT)) != 0)
#define Z_OPT_COLLECTABLE_P(zval_p) Z_OPT_COLLECTABLE(*(zval_p))
/* deprecated: (COPYABLE is the same as IS_ARRAY) */
#define Z_OPT_COPYABLE(zval) (Z_OPT_TYPE(zval) == IS_ARRAY)
#define Z_OPT_COPYABLE_P(zval_p) Z_OPT_COPYABLE(*(zval_p))