8

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

但是,似乎没有办法做到这一点 - 您必须复制值才能从 PHP 扩展函数返回它。将函数签名更改为通过引用返回值不是一种选择,因为它将属性和返回值联系起来 - 即稍后更改返回值,也会更改属性。这不是可接受的行为。

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

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

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

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

4

3 回答 3

7

如果您的函数按值返回,这只能从 PHP 5.6(当前主版本)开始使用RETURN_ZVAL_FAST宏:

RETURN_ZVAL_FAST(*myArrayProperty);

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

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);
}
于 2013-08-01T11:05:04.553 回答
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);

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

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

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

于 2015-10-26T17:09:17.833 回答
-1

已经有一段时间了,因为我编写了这样的代码……</p>

所以,我在下面的代码中做了什么: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);
}
于 2013-07-24T21:05:46.517 回答