2013-07-24 26 views
8

Sto sviluppando un'estensione PHP, in cui un metodo oggetto deve restituire un array zval.Come restituire un array da un'estensione PHP, senza copiarlo in memoria?

Il metodo appare come:

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

Il codice funziona bene e fa la cosa atteso - restituisce oggetto di myArrayProperty. Tuttavia, mi piacerebbe ottimizzare il processo.

myArrayProperty memorizza un array, che può essere abbastanza grande. E la macro RETURN_ZVAL() duplica quell'array per restituire il valore. Il processo di duplicazione richiede una buona quantità di tempo per acquisire memoria e copiare tutti i valori dell'array. Allo stesso tempo, l'array restituito viene solitamente utilizzato per operazioni di sola lettura. Quindi una buona ottimizzazione sarebbe quella di utilizzare il meccanismo di PHP con il conteggio dei riferimenti e non duplicare i contenuti di myArrayProperty. Piuttosto vorrei aumentare refcount di myArrayProperty e solo tornare puntatore ad esso. Questa è la stessa tattica usata normalmente, quando si lavora con variabili in un'estensione PHP.

Tuttavia, sembra, non c'è modo di farlo - è necessario duplicare il valore per restituirlo da una funzione di estensione PHP. La modifica della firma della funzione per restituire il valore per riferimento, non è un'opzione, poiché collega la proprietà e il valore restituito, ovvero modificando successivamente il valore restituito, modifica anche la proprietà. Questo non è un comportamento accettabile.

L'incapacità di impegnarsi conteggio dei riferimenti aspetto strano, perché stesso codice in PHP:

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

è ottimizzata dal meccanismo di conteggio di riferimento. Ecco perché sto facendo questa domanda a StackOverflow nel caso in cui mi sia sfuggito qualcosa.

Quindi, c'è un modo per restituire un array da una funzione nell'estensione PHP, senza copiare l'array in memoria?

risposta

-1

E 'passato un po', dal momento che ho codificato qualcosa di simile ...

Quindi, quello che faccio nel codice qui sotto: 1). aumento in modo esplicito refcounter 2). tornando zval senza copiarlo

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

Ma non sta per portare a una perdita di memoria o segfault (a seconda di quale viene prima)? Perdita di memoria avverrà quando tutti i riferimenti alla proprietà saranno cancellati, ma la memoria, occupata dal suo contenitore zval non può essere liberata, perché il Refcount rimarrebbe comunque 1. Segfault accadrà, quando il valore restituito sarà eliminato, quindi il suo zval il contenitore viene cancellato insieme all'array (hashTable condiviso, a cui si fa riferimento sia da quel contenitore che dal contenitore zval di proprietà), quindi l'uso di proprietà in seguito porterà a effetti imprevedibili, ma sicuramente sbagliati. –

+0

Riferimenti di proprietà zval - questo è il conto = 1. questo codice aumenta il conteggio, dato che zval viene restituito e sarà referenziato da entrambi gli oggetti e dal chiamante. se l'oggetto non ha bisogno di proprietà diminuirà il conto, quindi sarà di proprietà solo del chiamante. quindi, questo codice mi sembra sano di mente. ma, ancora una volta, tutto ciò è strettamente teorico - non ho ancora scaricato PHP in questo momento – JimiDini

+0

sfortunatamente, come previsto, il codice non funziona - confermato nella pratica: http://pastebin.com/FRfaJZvL. Il problema è come detto sopra: l'oggetto e il chiamante fanno riferimento a posizioni di memoria differenti. –

7

se la funzione restituisce per valore questo è possibile solo a partire da PHP 5.6 (master corrente) utilizzando i RETURN_ZVAL_FAST macro:

RETURN_ZVAL_FAST(*myArrayProperty); 

se la vostra funzione ritorna per riferimento (return_reference=1 nel arginfo) è possibile restituire utilizzando il seguente codice:

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

se la funzione restituisce per valore e siete sulla PHP 5.5 o vecchio ER è ancora possibile ottimizzare il caso 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); 
} 
+0

Bene, come si dice nella descrizione, la funzione non restituisce il valore per riferimento. Quindi non è una soluzione, sfortunatamente. –

+0

Scusa, mi sono perso. In questo caso ciò che vuoi non è possibile. – NikiC

+0

Anche se non vedo una ragione immediata per cui non potremmo passare in return_value_ptr anche senza ACC_RETURN_REFERENCE (a parte il fatto che ciò consentirebbe di restituire un is_ref = 1 zval da una funzione non ref). Potresti voler chiedere a internals @ a riguardo. – NikiC

0

Non ho accesso a PHP 5.6 < ma penso che il problema è che il valore viene copiato solo. Per essere assolutamente sicuro di dover cercare il codice per le definizioni in questione.

Questo significa che si potrebbe essere in grado di provare:

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

Questo è pericoloso se usato incautamente. Se usato con i tuoi contenitori temporanei, non conosco alcun problema.

Probabilmente è anche possibile lavorare direttamente sul valore di ritorno che potrebbe essere un approccio migliore. È probabile inizializzarlo e passarlo come un puntatore all'inizio.

Puoi avvolgere il tuo risultato di ritorno in questo modo. Puoi anche sperimentare con i riferimenti.