c-如何从PHP扩展返回数组,而不将其复制到内存中


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

我正在开发一个PHP扩展,其中一个对象方法需要返回一个数组zval

方法看起来像:

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);
}

该代码运行良好,并完成了预期的操作——它返回对象的myArrayProperty。不过,我想优化流程。

myArrayProperty存储一个数组,这个数组可能很大。RETURN_ZVAL()宏复制该数组以便返回值。复制过程需要大量时间来获取内存并复制所有数组值。同时,返回的数组通常用于只读操作。因此,一个很好的优化是使用PHP的引用计数机制,并且不重复myArrayProperty内容。相反,我会增加myArrayPropertyrefcount,并返回指向它的指针。这与在PHP扩展中处理变量时通常使用的策略相同。

然而,似乎并没有办法做到这一点——您必须复制值才能从PHP扩展函数返回它。将函数签名更改为通过引用返回值不是一个选项,因为它链接了属性和返回值,即稍后更改返回值也会更改属性。这是不可接受的行为。

无法参与引用计数看起来很奇怪,因为PHP中的代码相同:

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

通过参考计数机制进行优化。这就是为什么我在StackOverflow上问这个问题,以防我错过了什么。

那么,有没有一种方法可以从PHP扩展中的函数返回数组,而不在内存中复制数组

如果函数按值返回,则只有在PHP 5.6(当前主程序)使用RETURN_ZVAL_FAST宏时才可能返回:

RETURN_ZVAL_FAST(*myArrayProperty);

如果函数通过引用返回(arginfo中的return_reference=1),则可以使用以下代码返回:

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

如果你的函数按值返回,并且你使用的是PHP 5.5或更高版本,你仍然可以优化refcount=1的情况:

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);
}

我没有访问PHP<5.6但我认为问题在于,该值只是被复制的。为了绝对确定,您应该在代码中搜索有问题的定义。

这意味着你可以尝试:

 zval *arr;
 MAKE_STD_ZVAL(arr);
 array_init(arr);
 // Do things to the array.
 RETVAL_ZVAL(arr, 0, 0);
 efree(arr);

如果使用不当,这是危险的。如果与您自己的临时容器一起使用,我不知道有什么问题。

您也可以直接处理返回值,这可能是一种更好的方法。您可能会初始化它,并在开始时将其作为指针传递。

您可以这样包装您的返回结果。你也可以尝试参考文献。

我编码了这样的东西已经有一段时间了…

所以,我在下面的代码中做了什么:1)。显式增加refcounter 2)。返回zval而不复制它

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