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

时间:2013-07-24 20:39:38

标签: php c php-extension php-internals

我正在开发一个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内容。相反,我会增加refcount的{​​{1}}并返回指向它的指针。当使用PHP扩展中的变量时,这与通常使用的策略相同。

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

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

myArrayProperty

通过引用计数机制进行优化。这就是我在StackOverflow上提出这个问题的原因,以防我错过了什么。

那么,有没有办法从PHP扩展中的函数返回数组,而不将数组复制到内存中?

3 个答案:

答案 0 :(得分:7)

如果您的函数返回值,则只能使用RETURN_ZVAL_FAST宏从PHP 5.6(当前主数据库)开始:

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

答案 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);

如果使用不明智,这很危险。如果与您自己的临时容器一起使用,我不知道有任何问题。

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

您可以像这样包装返回结果。您也可以尝试参考。

答案 2 :(得分:-1)

已经有一段时间了,因为我编写了类似这样的内容......

那么,我在下面的代码中做了什么: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);
}