我正在开发一个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扩展中的函数返回数组,而不将数组复制到内存中?
答案 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);
}