mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Improve performance of unpack() with nameless repetitions (#18803)
We can avoid creating temporary strings, and then reparsing them into
numbers with zend_symtable_update() by using zend_hash_index_update()
directly.
For the following benchmark on an i7-4790:
```php
$file = str_repeat('A', 100000);
for ($i=0;$i<100;$i++) unpack('C*',$file);
```
I get:
```
Benchmark 1: ./sapi/cli/php y.php
Time (mean ± σ): 85.8 ms ± 1.8 ms [User: 74.5 ms, System: 10.4 ms]
Range (min … max): 83.8 ms … 92.4 ms 33 runs
Benchmark 2: ./sapi/cli/php_old y.php
Time (mean ± σ): 318.3 ms ± 2.7 ms [User: 306.7 ms, System: 9.9 ms]
Range (min … max): 314.9 ms … 321.6 ms 10 runs
Summary
./sapi/cli/php y.php ran
3.71 ± 0.08 times faster than ./sapi/cli/php_old y.php
```
On an i7-1185G7 I get:
```
Benchmark 1: ./sapi/cli/php test.php
Time (mean ± σ): 60.1 ms ± 0.7 ms [User: 47.8 ms, System: 12.0 ms]
Range (min … max): 59.2 ms … 63.8 ms 48 runs
Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.
Benchmark 2: ./sapi/cli/php_old test.php
Time (mean ± σ): 220.8 ms ± 2.2 ms [User: 209.6 ms, System: 10.7 ms]
Range (min … max): 218.5 ms … 224.5 ms 13 runs
Summary
./sapi/cli/php test.php ran
3.67 ± 0.06 times faster than ./sapi/cli/php_old test.php
```
This commit is contained in:
@@ -582,6 +582,8 @@ PHP 8.5 UPGRADE NOTES
|
||||
. Improved performance of array functions with callbacks
|
||||
(array_find, array_filter, array_map, usort, ...).
|
||||
. Improved performance of urlencode() and rawurlencode().
|
||||
. Improved unpack() performance with nameless repetitions by avoiding
|
||||
creating temporary strings and reparsing them.
|
||||
|
||||
- XMLReader:
|
||||
. Improved property access performance.
|
||||
|
||||
@@ -885,12 +885,15 @@ PHP_FUNCTION(unpack)
|
||||
if ((inputpos + size) <= inputlen) {
|
||||
|
||||
zend_string* real_name;
|
||||
zend_long long_key = 0;
|
||||
zval val;
|
||||
|
||||
if (repetitions == 1 && namelen > 0) {
|
||||
if (namelen == 0) {
|
||||
real_name = NULL;
|
||||
long_key = i + 1;
|
||||
} else if (repetitions == 1) {
|
||||
/* Use a part of the formatarg argument directly as the name. */
|
||||
real_name = zend_string_init_fast(name, namelen);
|
||||
|
||||
} else {
|
||||
/* Need to add the 1-based element number to the name */
|
||||
char buf[MAX_LENGTH_OF_LONG + 1];
|
||||
@@ -912,7 +915,6 @@ PHP_FUNCTION(unpack)
|
||||
size = len;
|
||||
|
||||
ZVAL_STRINGL(&val, &input[inputpos], len);
|
||||
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
|
||||
break;
|
||||
}
|
||||
case 'A': {
|
||||
@@ -939,7 +941,6 @@ PHP_FUNCTION(unpack)
|
||||
}
|
||||
|
||||
ZVAL_STRINGL(&val, &input[inputpos], len + 1);
|
||||
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
|
||||
break;
|
||||
}
|
||||
/* New option added for Z to remain in-line with the Perl implementation */
|
||||
@@ -964,7 +965,6 @@ PHP_FUNCTION(unpack)
|
||||
len = s;
|
||||
|
||||
ZVAL_STRINGL(&val, &input[inputpos], len);
|
||||
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -979,7 +979,9 @@ PHP_FUNCTION(unpack)
|
||||
|
||||
|
||||
if (size > INT_MAX / 2) {
|
||||
zend_string_release(real_name);
|
||||
if (real_name) {
|
||||
zend_string_release_ex(real_name, false);
|
||||
}
|
||||
zend_argument_value_error(1, "repeater must be less than or equal to %d", INT_MAX / 2);
|
||||
RETURN_THROWS();
|
||||
}
|
||||
@@ -1016,7 +1018,6 @@ PHP_FUNCTION(unpack)
|
||||
ZSTR_VAL(buf)[len] = '\0';
|
||||
|
||||
ZVAL_STR(&val, buf);
|
||||
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1026,7 +1027,6 @@ PHP_FUNCTION(unpack)
|
||||
zend_long v = (type == 'c') ? (int8_t) x : x;
|
||||
|
||||
ZVAL_LONG(&val, v);
|
||||
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1046,7 +1046,6 @@ PHP_FUNCTION(unpack)
|
||||
}
|
||||
|
||||
ZVAL_LONG(&val, v);
|
||||
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1062,7 +1061,6 @@ PHP_FUNCTION(unpack)
|
||||
}
|
||||
|
||||
ZVAL_LONG(&val, v);
|
||||
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1082,8 +1080,6 @@ PHP_FUNCTION(unpack)
|
||||
}
|
||||
|
||||
ZVAL_LONG(&val, v);
|
||||
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1104,7 +1100,6 @@ PHP_FUNCTION(unpack)
|
||||
}
|
||||
|
||||
ZVAL_LONG(&val, v);
|
||||
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@@ -1124,7 +1119,6 @@ PHP_FUNCTION(unpack)
|
||||
}
|
||||
|
||||
ZVAL_DOUBLE(&val, v);
|
||||
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1143,13 +1137,12 @@ PHP_FUNCTION(unpack)
|
||||
}
|
||||
|
||||
ZVAL_DOUBLE(&val, v);
|
||||
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'x':
|
||||
/* Do nothing with input, just skip it */
|
||||
break;
|
||||
goto no_output;
|
||||
|
||||
case 'X':
|
||||
if (inputpos < size) {
|
||||
@@ -1160,7 +1153,7 @@ PHP_FUNCTION(unpack)
|
||||
php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
|
||||
}
|
||||
}
|
||||
break;
|
||||
goto no_output;
|
||||
|
||||
case '@':
|
||||
if (repetitions <= inputlen) {
|
||||
@@ -1170,10 +1163,19 @@ PHP_FUNCTION(unpack)
|
||||
}
|
||||
|
||||
i = repetitions - 1; /* Done, break out of for loop */
|
||||
break;
|
||||
goto no_output;
|
||||
}
|
||||
|
||||
zend_string_release(real_name);
|
||||
if (real_name) {
|
||||
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
|
||||
} else {
|
||||
zend_hash_index_update(Z_ARRVAL_P(return_value), long_key, &val);
|
||||
}
|
||||
|
||||
no_output:
|
||||
if (real_name) {
|
||||
zend_string_release_ex(real_name, false);
|
||||
}
|
||||
|
||||
inputpos += size;
|
||||
if (inputpos < 0) {
|
||||
|
||||
Reference in New Issue
Block a user