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

json: Improve performance of php_json_encode_array() (#20092)

Instead of using a boolean flag to check for each element whether or not it is
the first, we just unconditionally append a comma after each element and then
remove the last comma at the end.

For:

    <?php

    $len = 0;
    for ($i = 0; $i < 3_000_000; $i++) {
    	$len += strlen(json_encode(array_fill(0, 20, [])));
    }
    var_dump($len);

This is ~1.06 faster for a gcc 13.3 release build on a Intel(R) Core(TM)
i7-1365U.

    Benchmark 1: /tmp/bench/before /tmp/bench/test6.php
      Time (mean ± σ):     819.6 ms ±   2.8 ms    [User: 816.4 ms, System: 2.4 ms]
      Range (min … max):   816.9 ms … 825.0 ms    10 runs

    Benchmark 2: /tmp/bench/after /tmp/bench/test6.php
      Time (mean ± σ):     770.8 ms ±   5.8 ms    [User: 766.6 ms, System: 2.9 ms]
      Range (min … max):   765.3 ms … 785.8 ms    10 runs

    Summary
      /tmp/bench/after /tmp/bench/test6.php ran
        1.06 ± 0.01 times faster than /tmp/bench/before /tmp/bench/test6.php
This commit is contained in:
Tim Düsterhus
2025-10-08 10:10:16 +02:00
committed by GitHub
parent f905950ba5
commit 18d99ee4b7
2 changed files with 22 additions and 26 deletions

View File

@@ -84,3 +84,6 @@ PHP 8.6 UPGRADE NOTES
========================================
14. Performance Improvements
========================================
- JSON:
. Improve performance of encoding arrays and objects.

View File

@@ -109,7 +109,6 @@ static inline void php_json_encode_double(smart_str *buf, double d, int options)
static zend_result php_json_encode_array(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
{
bool encode_as_object = options & PHP_JSON_FORCE_OBJECT;
bool need_comma = false;
HashTable *myht, *prop_ht;
zend_refcounted *recursion_rc;
@@ -161,12 +160,6 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
continue;
}
if (need_comma) {
smart_str_appendc(buf, ',');
} else {
need_comma = true;
}
php_json_pretty_print_char(buf, options, '\n');
php_json_pretty_print_indent(buf, options, encoder);
@@ -186,6 +179,14 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
PHP_JSON_HASH_UNPROTECT_RECURSION(obj);
return FAILURE;
}
smart_str_appendc(buf, ',');
}
bool empty = ZSTR_VAL(buf->s)[ZSTR_LEN(buf->s) - 1] != ',';
if (!empty) {
/* Drop the trailing comma. */
ZSTR_LEN(buf->s)--;
}
PHP_JSON_HASH_UNPROTECT_RECURSION(obj);
@@ -197,7 +198,7 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
}
--encoder->depth;
if (need_comma) {
if (!empty) {
php_json_pretty_print_char(buf, options, '\n');
php_json_pretty_print_indent(buf, options, encoder);
}
@@ -235,6 +236,7 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
uint32_t i = myht ? zend_hash_num_elements(myht) : 0;
bool empty = true;
if (i > 0) {
zend_string *key;
zval *data;
@@ -247,12 +249,6 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
if (!encode_as_object) {
ZEND_ASSERT(Z_TYPE_P(data) != IS_PTR);
if (need_comma) {
smart_str_appendc(buf, ',');
} else {
need_comma = true;
}
php_json_pretty_print_char(buf, options, '\n');
php_json_pretty_print_indent(buf, options, encoder);
} else {
@@ -276,11 +272,6 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
}
}
if (need_comma) {
smart_str_appendc(buf, ',');
} else {
need_comma = true;
}
php_json_pretty_print_char(buf, options, '\n');
php_json_pretty_print_indent(buf, options, encoder);
@@ -293,12 +284,6 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
smart_str_appendl(buf, "\"\"", 2);
}
} else {
if (need_comma) {
smart_str_appendc(buf, ',');
} else {
need_comma = true;
}
php_json_pretty_print_char(buf, options, '\n');
php_json_pretty_print_indent(buf, options, encoder);
@@ -319,7 +304,15 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
return FAILURE;
}
zval_ptr_dtor(&tmp);
smart_str_appendc(buf, ',');
} ZEND_HASH_FOREACH_END();
empty = ZSTR_VAL(buf->s)[ZSTR_LEN(buf->s) - 1] != ',';
if (!empty) {
/* Drop the trailing comma. */
ZSTR_LEN(buf->s)--;
}
}
PHP_JSON_HASH_UNPROTECT_RECURSION(recursion_rc);
@@ -334,7 +327,7 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
--encoder->depth;
/* Only keep closing bracket on same line for empty arrays/objects */
if (need_comma) {
if (!empty) {
php_json_pretty_print_char(buf, options, '\n');
php_json_pretty_print_indent(buf, options, encoder);
}