Skip to content

Commit 7508f8b

Browse files
committed
Improve performance of unpack() with nameless repetitions
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 ```
1 parent cb04226 commit 7508f8b

File tree

2 files changed

+19
-17
lines changed

2 files changed

+19
-17
lines changed

UPGRADING

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,8 @@ MBString:
567567
. Improved performance of array functions with callbacks
568568
(array_find, array_filter, array_map, usort, ...).
569569
. Improved performance of urlencode() and rawurlencode().
570+
. Improved unpack() performance with nameless repetitions by avoiding
571+
creating temporary strings and reparsing them.
570572

571573
- XMLReader:
572574
. Improved property access performance.

ext/standard/pack.c

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -885,12 +885,15 @@ PHP_FUNCTION(unpack)
885885
if ((inputpos + size) <= inputlen) {
886886

887887
zend_string* real_name;
888+
zend_long long_key = 0;
888889
zval val;
889890

890891
if (repetitions == 1 && namelen > 0) {
891892
/* Use a part of the formatarg argument directly as the name. */
892893
real_name = zend_string_init_fast(name, namelen);
893-
894+
} else if (namelen == 0) {
895+
real_name = NULL;
896+
long_key = i + 1;
894897
} else {
895898
/* Need to add the 1-based element number to the name */
896899
char buf[MAX_LENGTH_OF_LONG + 1];
@@ -912,7 +915,6 @@ PHP_FUNCTION(unpack)
912915
size = len;
913916

914917
ZVAL_STRINGL(&val, &input[inputpos], len);
915-
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
916918
break;
917919
}
918920
case 'A': {
@@ -939,7 +941,6 @@ PHP_FUNCTION(unpack)
939941
}
940942

941943
ZVAL_STRINGL(&val, &input[inputpos], len + 1);
942-
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
943944
break;
944945
}
945946
/* New option added for Z to remain in-line with the Perl implementation */
@@ -964,7 +965,6 @@ PHP_FUNCTION(unpack)
964965
len = s;
965966

966967
ZVAL_STRINGL(&val, &input[inputpos], len);
967-
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
968968
break;
969969
}
970970

@@ -1016,7 +1016,6 @@ PHP_FUNCTION(unpack)
10161016
ZSTR_VAL(buf)[len] = '\0';
10171017

10181018
ZVAL_STR(&val, buf);
1019-
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
10201019
break;
10211020
}
10221021

@@ -1026,7 +1025,6 @@ PHP_FUNCTION(unpack)
10261025
zend_long v = (type == 'c') ? (int8_t) x : x;
10271026

10281027
ZVAL_LONG(&val, v);
1029-
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
10301028
break;
10311029
}
10321030

@@ -1046,7 +1044,6 @@ PHP_FUNCTION(unpack)
10461044
}
10471045

10481046
ZVAL_LONG(&val, v);
1049-
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
10501047
break;
10511048
}
10521049

@@ -1062,7 +1059,6 @@ PHP_FUNCTION(unpack)
10621059
}
10631060

10641061
ZVAL_LONG(&val, v);
1065-
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
10661062
break;
10671063
}
10681064

@@ -1082,8 +1078,6 @@ PHP_FUNCTION(unpack)
10821078
}
10831079

10841080
ZVAL_LONG(&val, v);
1085-
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
1086-
10871081
break;
10881082
}
10891083

@@ -1104,7 +1098,6 @@ PHP_FUNCTION(unpack)
11041098
}
11051099

11061100
ZVAL_LONG(&val, v);
1107-
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
11081101
break;
11091102
}
11101103
#endif
@@ -1124,7 +1117,6 @@ PHP_FUNCTION(unpack)
11241117
}
11251118

11261119
ZVAL_DOUBLE(&val, v);
1127-
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
11281120
break;
11291121
}
11301122

@@ -1143,13 +1135,12 @@ PHP_FUNCTION(unpack)
11431135
}
11441136

11451137
ZVAL_DOUBLE(&val, v);
1146-
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
11471138
break;
11481139
}
11491140

11501141
case 'x':
11511142
/* Do nothing with input, just skip it */
1152-
break;
1143+
goto no_output;
11531144

11541145
case 'X':
11551146
if (inputpos < size) {
@@ -1160,7 +1151,7 @@ PHP_FUNCTION(unpack)
11601151
php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
11611152
}
11621153
}
1163-
break;
1154+
goto no_output;
11641155

11651156
case '@':
11661157
if (repetitions <= inputlen) {
@@ -1170,10 +1161,19 @@ PHP_FUNCTION(unpack)
11701161
}
11711162

11721163
i = repetitions - 1; /* Done, break out of for loop */
1173-
break;
1164+
goto no_output;
11741165
}
11751166

1176-
zend_string_release(real_name);
1167+
if (real_name) {
1168+
zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
1169+
} else {
1170+
zend_hash_index_update(Z_ARRVAL_P(return_value), long_key, &val);
1171+
}
1172+
1173+
no_output:
1174+
if (real_name) {
1175+
zend_string_release_ex(real_name, false);
1176+
}
11771177

11781178
inputpos += size;
11791179
if (inputpos < 0) {

0 commit comments

Comments
 (0)