rfc:fast_zpp

This is an old revision of the document!


PHP RFC: Fast Parameter Parsing API

Introduction

PHP internal functions use zend_parse_parameters() API to receive values of actual parameters into C variables. This function uses scanf() like approach for parameter definition. The number and types of required data defined by a string that contains a list of specifiers (“s” - for string, “l” for long, etc). Unfortunately, this string has to be parsed each time when php calls this function, and this makes significant performance overhead.

For example zend_parse_parameters() takes ~6% of CPU time in serving wordpress home page.

For some really simple function like is_string() or ord() the overhead of zend_parse_parameters() may be about 90%.

Proposal

We propose an additional fast API for parameter parsing, that should be used for the most useful functions. The API is based on C macros that lead to inlining of optimized code directly into the body of internal function.

I'll explain API on the following example:

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al|zb", &input, &offset, &z_length, &preserve_keys) == FAILURE) {
	return;
}
ZEND_PARSE_PARAMETERS_START(2, 4)
	Z_PARAM_ARRAY(input)
	Z_PARAM_LONG(offset)
	Z_PARAM_OPTIONAL
	Z_PARAM_ZVAL(z_length)
	Z_PARAM_BOOL(preserve_keys)
ZEND_PARSE_PARAMETERS_END();

The first code fragment is just taken from PHP_FUNCTION(array_slice), the second is its replacement using new API. The code is actually self explainable.

ZEND_PARSE_PARAMETERS_START() takes two arguments minimal and maximal parameters count. (In general, it's possible to avoid these arguments, but not for free. I'll explain it separate “Simpler Variation” section).

Z_PARAM_ARRAY() - takes the next argument as array, Z_PARAM_LONG - as long, Z_PARAM_OPTIONAL - tells that the remaining arguments are optional.

The new API covers all possibilities of the existing API. The following table shows the correspondence between old specifiers and new macros.

specifier Fast ZPP API macro
| Z_PARAM_OPTIONAL
a Z_PARAM_ARRAY(dest) dest - zval*
A Z_PARAM_ARRAY_OR_OBJECT(dest) dest - zval*
b Z_PARAM_BOOL(dest) dest - zend_bool
C Z_PARAM_CLASS(dest) dest - zend_class_entry*
d Z_PARAM_DOUBLE(dest) dest - double
f Z_PARAM_FUNC(fci, fcc) fci - zend_fcall_info, fcc - zend_fcall_info_cache
h Z_PARAM_ARRAY_HT(dest) dest - HashTable*
H Z_PARAM_ARRAY_OR_OBJECT_HT(dest) dest - HashTable*
l Z_PARAM_LONG(dest) dest - long
L Z_PARAM_STRICT_LONG(dest) dest - long
o Z_PARAM_OBJECT(dest) dest - zval*
O Z_PARAM_OBJECT_OF_CLASS(dest, ce) dest - zval*
p Z_PARAM_PATH(dest, dest_len) dest - char*, dest_len - int
P Z_PARAM_PATH_STR(dest) dest - zend_string*
r Z_PARAM_RESOURCE(dest) dest - zval*
s Z_PARAM_STRING(dest, dest_len) dest - char*, dest_len - int
S Z_PARAM_STR(dest) dest - zend_string*
z Z_PARAM_ZVAL(dest) dest - zval*
Z_PARAM_ZVAL_DEREF(dest) dest - zval*
+ Z_PARAM_VARIADIC('+', dest, num) dest - zval*, num int
* Z_PARAM_VARIADIC('*', dest, num) dest - zval*, num int

The effect of ! and / modifiers may be achieved using extended version of the same macros e.g. Z_PARAM_ZVAL(dest, check_null, separate).

Simpler Variation

It's possible not to specify minimal and maximal arguments in ZEND_PARSE_PARAMETERS_START() macro, but it leads to some small performance degradation and possible inconsistent error messages when few constraint are broken at same time. For example the following code strlen(array(), 1) would emit different warning:

Warning: strlen() expects parameter 1 to be string

however the original PHP would emit

Warning: strlen() expects exactly 1 parameter, 2 given

This is not a common case. Actually, it breaks just 2 PHPT tests. So the penalties may be accepted in cost of usability. On the other hand these number need to be written just once...

Performance Evaluation

The following numers collected for 100 and 1000 requests to wordpress home page.

# of CPU instructions time [sec]
phpng 5,132,181,672 18.90
phpng + fast_zpp 4,901,907,204 18.40
phpng + simple variation 4,906,486,678 18.40

So the proposal provides 4.5% improvement in terms of CPU instructions and 2.5% in terms of time.

The effect of code size increase is negligible - Jist ~20KB for ~70 function (that's about 0.1%)

Backward Incompatible Changes

The new API provides exactly the same behavior. Also, we don't propose to remove the old API. So there must not be any compatibility problems.

Proposed PHP Version(s)

This proposal is targeted to phpng

Proposed Voting Choices

  • no
  • yes
  • yes for simpler variation

State whether this project requires 50%+1 majority (see voting)

Patches and Tests

rfc/fast_zpp.1400844725.txt.gz · Last modified: 2017/09/22 13:28 (external edit)