2013-07-24 4 views
8

Ich entwickle eine PHP-Erweiterung, bei der eine Objektmethode ein Array zval zurückgeben muss.Wie man ein Array von einer PHP-Erweiterung zurückgibt, ohne es in den Speicher zu kopieren?

Das Verfahren sieht so aus:

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

Der Code funktioniert gut und hat die erwartete Sache - es gibt Objekts myArrayProperty. Ich möchte jedoch den Prozess optimieren.

myArrayProperty speichert ein Array, das ziemlich groß sein kann. Und das Makro RETURN_ZVAL() dupliziert dieses Array, um den Wert zurückzugeben. Der Duplizierungsprozess benötigt viel Zeit, um Speicher zu erhalten und alle Array-Werte zu kopieren. Zur gleichen Zeit wird das zurückgegebene Array normalerweise für schreibgeschützte Operationen verwendet. Eine schöne Optimierung wäre also, den Mechanismus von PHP mit Referenzzählung zu verwenden und myArrayProperty Inhalte nicht zu duplizieren. Stattdessen würde ich refcount von myArrayProperty erhöhen und Zeiger nur darauf zurückgeben. Dies ist die gleiche Taktik wie beim Arbeiten mit Variablen in einer PHP-Erweiterung.

Es scheint jedoch, gibt es keine Möglichkeit, es zu tun - Sie müssen Wert zu duplizieren, um es aus einer PHP-Erweiterungsfunktion zurückgeben. Das Ändern der Funktionssignatur, um den Wert als Referenz zurückzugeben, ist keine Option, da sie die Eigenschaft und den zurückgegebenen Wert verbindet, d. H. Den zurückgegebenen Wert später ändert, ändert sich auch die Eigenschaft. Das ist kein akzeptables Verhalten.

Die Unfähigkeit Referenzzählung zu engagieren sieht seltsam aus, weil gleiche Code in PHP:

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

durch den Referenz Zählmechanismus optimiert ist. Deshalb stelle ich diese Frage bei StackOverflow, falls ich etwas verpasst habe.

Gibt es also eine Möglichkeit, ein Array von einer Funktion in der PHP-Erweiterung zurückzugeben, ohne das Array im Speicher zu kopieren?

Antwort

-1

Es ist eine Weile gewesen, da ich so etwas wie diese codiert ...

Also, was ich in Code tue unten: 1). explizit erhöhender Refcounter 2). Rückkehr zval es ohne das Kopieren

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

Aber wird es nicht zu einem Speicherleck oder einem Fehler führen (was auch immer zuerst eintritt)? Ein Speicherleck tritt auf, wenn alle Verweise auf die Eigenschaft gelöscht werden, aber der Speicher, der von seinem zval-Container belegt ist, kann nicht freigegeben werden, da der refcount immer noch 1 bleibt. Segfault wird passieren, wenn der zurückgegebene Wert entsorgt wird, also zval Der Container wird zusammen mit dem Array gelöscht (shared HashTable, auf das sowohl vom Container als auch vom Container "property zval" verwiesen wird), sodass die Verwendung der Eigenschaft später zu unvorhersehbaren, aber definitiv falschen Auswirkungen führt. –

+0

Eigenschaftenreferenzen zval - das ist refcount = 1. Dieser Code erhöht refcount, da zval zurückgegeben wird und sowohl vom Objekt als auch vom Aufrufer referenziert wird. Wenn das Objekt keine Eigenschaft benötigt, verringert es den Refcount, sodass es nur dem Aufrufer gehört. also, dieser Code sieht für mich vernünftig aus. aber, wieder, all dies ist streng theoretisch - ich habe nicht einmal Quellcode von PHP gerade ausgepackt – JimiDini

+0

leider, wie vorhergesagt, funktioniert der Code nicht - in der Praxis bestätigt: http://pastebin.com/FRFAJZvL. Das Problem ist wie oben gesagt: Das Objekt und der Anrufer verweisen auf verschiedene Speicherorte. –

7

Wenn Ihre Funktion kehrt nach Wert dies erst ab PHP 5.6 (aktuelle Master) ist mit der RETURN_ZVAL_FAST Makro:

RETURN_ZVAL_FAST(*myArrayProperty); 

Wenn Ihre Funktion kehrt durch Verweis (return_reference=1 im arginfo) Sie können zurückkehren mit dem folgenden Code:

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

Wenn Ihre Funktion kehrt nach-Wert und Sie sind auf PHP 5.5 oder alt er kann man immer noch den refcount=1 Fall optimieren:

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

Nun, wie es in der Beschreibung heißt - die Funktion gibt keinen Wert als Referenz zurück. Es ist also leider keine Lösung. –

+0

Entschuldigung, das habe ich verpasst. In diesem Fall ist das, was Sie wollen, nicht möglich. – NikiC

+0

Obwohl ich keinen unmittelbaren Grund sehe, warum wir nicht in return_value_ptr auch ohne ACC_RETURN_REFERENCE übergeben konnten (abgesehen von der Tatsache, dass Sie damit ein is_ref = 1 zval von einer Nicht-Ref-Funktion zurückgeben könnten). Vielleicht möchtest du dich zu Internals @ danach erkundigen. – NikiC

0

ich keinen Zugang zu PHP < 5.6 habe tun, aber ich denke, das Problem ist, dass der Wert nur kopiert wird. Um absolut sicher zu sein, sollten Sie den Code nach den betreffenden Definitionen durchsuchen.

Das heißt, Sie könnten in der Lage sein, zu versuchen:

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

Diese gefährlich ist, wenn unbedacht verwendet. Bei Verwendung mit eigenen temporären Containern sind mir keine Probleme bekannt.

Sie können wahrscheinlich auch direkt am Rückgabewert arbeiten, was ein besserer Ansatz sein könnte. Sie würden es wahrscheinlich initialisieren und es als Zeiger am Anfang weitergeben.

Sie können Ihre Rückmeldung so einpacken. Sie können auch mit Referenzen experimentieren.

Verwandte Themen