Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return array from a PHP extension, without copying it in memory?

I'm developing a PHP-extension, where an object method needs to return an array zval.

The method looks like:

ZEND_METHOD(myObject, myMethod)
{
    zval **myArrayProperty;
    if (zend_hash_find(Z_OBJPROP_P(getThis()), "myArrayProperty", sizeof("myArrayProperty"), (void **) &myArrayProperty) == FAILURE) {
        RETURN_FALSE;
    }
    RETURN_ZVAL(*myArrayProperty, 1, 0);
}

The code works fine and does the expected thing - it returns object's myArrayProperty. However, I'd like to optimize the process.

myArrayProperty stores an array, which can be quite big. And the RETURN_ZVAL() macro duplicates that array in order to return the value. The duplication process takes good amount of time to acquire memory and copy all the array values. At the same time, the returned array is usually used for read-only operations. So a nice optimization would be to use PHP's mechanism with reference counting and do not duplicate myArrayProperty contents. Rather I'd increase refcount of myArrayProperty and just return pointer to it. This is the same tactic as usually used, when working with variables in a PHP extension.

However, seems, there is no way to do it - you have to duplicate value in order to return it from a PHP extension function. Changing function signature to return value by reference, is not an option, because it links the property and returned value - i.e. changing returned value later, changes the property as well. That is not an acceptable behavior.

The inability to engage reference counting looks strange, because same code in PHP:

function myMethod() {
{
    return $this->myArrayProperty;
}

is optimized by the reference counting mechanism. That's why I'm asking this question at StackOverflow in case I missed something.

So, is there a way to return an array from a function in PHP extension, without copying the array in memory?

like image 424
Andrey Tserkus Avatar asked Jul 24 '13 20:07

Andrey Tserkus


1 Answers

If your function returns by-value this is only possible as of PHP 5.6 (current master) using the RETURN_ZVAL_FAST macro:

RETURN_ZVAL_FAST(*myArrayProperty);

If your function returns by-reference (return_reference=1 in the arginfo) you can return using the following code:

zval_ptr_dtor(&return_value);
SEPARATE_ZVAL_TO_MAKE_IS_REF(myArrayProperty);
Z_ADDREF_PP(myArrayProperty);
*return_value_ptr = *myArrayProperty;

If your function returns by-value and you're on PHP 5.5 or older you can still optimize the refcount=1 case:

if (Z_REFCOUNT_PP(myArrayProperty) == 1) {
    RETVAL_ZVAL(*myArrayProperty, 0, 1);
    Z_ADDREF_P(return_value);
    *myArrayProperty = return_value;
} else {
    RETVAL_ZVAL(*myArrayProperty, 1, 0);
}
like image 157
NikiC Avatar answered Oct 02 '22 08:10

NikiC