Skip to content

Commit 18d99ee

Browse files
authored
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
1 parent f905950 commit 18d99ee

File tree

2 files changed

+22
-26
lines changed

2 files changed

+22
-26
lines changed

UPGRADING

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,6 @@ PHP 8.6 UPGRADE NOTES
8484
========================================
8585
14. Performance Improvements
8686
========================================
87+
88+
- JSON:
89+
. Improve performance of encoding arrays and objects.

ext/json/json_encoder.c

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ static inline void php_json_encode_double(smart_str *buf, double d, int options)
109109
static zend_result php_json_encode_array(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
110110
{
111111
bool encode_as_object = options & PHP_JSON_FORCE_OBJECT;
112-
bool need_comma = false;
113112
HashTable *myht, *prop_ht;
114113
zend_refcounted *recursion_rc;
115114

@@ -161,12 +160,6 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
161160
continue;
162161
}
163162

164-
if (need_comma) {
165-
smart_str_appendc(buf, ',');
166-
} else {
167-
need_comma = true;
168-
}
169-
170163
php_json_pretty_print_char(buf, options, '\n');
171164
php_json_pretty_print_indent(buf, options, encoder);
172165

@@ -186,6 +179,14 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
186179
PHP_JSON_HASH_UNPROTECT_RECURSION(obj);
187180
return FAILURE;
188181
}
182+
183+
smart_str_appendc(buf, ',');
184+
}
185+
186+
bool empty = ZSTR_VAL(buf->s)[ZSTR_LEN(buf->s) - 1] != ',';
187+
if (!empty) {
188+
/* Drop the trailing comma. */
189+
ZSTR_LEN(buf->s)--;
189190
}
190191

191192
PHP_JSON_HASH_UNPROTECT_RECURSION(obj);
@@ -197,7 +198,7 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
197198
}
198199
--encoder->depth;
199200

200-
if (need_comma) {
201+
if (!empty) {
201202
php_json_pretty_print_char(buf, options, '\n');
202203
php_json_pretty_print_indent(buf, options, encoder);
203204
}
@@ -235,6 +236,7 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
235236

236237
uint32_t i = myht ? zend_hash_num_elements(myht) : 0;
237238

239+
bool empty = true;
238240
if (i > 0) {
239241
zend_string *key;
240242
zval *data;
@@ -247,12 +249,6 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
247249
if (!encode_as_object) {
248250
ZEND_ASSERT(Z_TYPE_P(data) != IS_PTR);
249251

250-
if (need_comma) {
251-
smart_str_appendc(buf, ',');
252-
} else {
253-
need_comma = true;
254-
}
255-
256252
php_json_pretty_print_char(buf, options, '\n');
257253
php_json_pretty_print_indent(buf, options, encoder);
258254
} else {
@@ -276,11 +272,6 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
276272
}
277273
}
278274

279-
if (need_comma) {
280-
smart_str_appendc(buf, ',');
281-
} else {
282-
need_comma = true;
283-
}
284275

285276
php_json_pretty_print_char(buf, options, '\n');
286277
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,
293284
smart_str_appendl(buf, "\"\"", 2);
294285
}
295286
} else {
296-
if (need_comma) {
297-
smart_str_appendc(buf, ',');
298-
} else {
299-
need_comma = true;
300-
}
301-
302287
php_json_pretty_print_char(buf, options, '\n');
303288
php_json_pretty_print_indent(buf, options, encoder);
304289

@@ -319,7 +304,15 @@ static zend_result php_json_encode_array(smart_str *buf, zval *val, int options,
319304
return FAILURE;
320305
}
321306
zval_ptr_dtor(&tmp);
307+
308+
smart_str_appendc(buf, ',');
322309
} ZEND_HASH_FOREACH_END();
310+
311+
empty = ZSTR_VAL(buf->s)[ZSTR_LEN(buf->s) - 1] != ',';
312+
if (!empty) {
313+
/* Drop the trailing comma. */
314+
ZSTR_LEN(buf->s)--;
315+
}
323316
}
324317

325318
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,
334327
--encoder->depth;
335328

336329
/* Only keep closing bracket on same line for empty arrays/objects */
337-
if (need_comma) {
330+
if (!empty) {
338331
php_json_pretty_print_char(buf, options, '\n');
339332
php_json_pretty_print_indent(buf, options, encoder);
340333
}

0 commit comments

Comments
 (0)