2011-01-12 9 views
7

Esta es una pregunta de programación simple, que proviene de mi falta de conocimiento de cómo PHP maneja la copia y el desarmado de matrices durante un ciclo foreach. Es así, tengo una matriz que viene de una fuente externa formateada de una manera que quiero cambiar. Un ejemplo sencillo sería:¿Desea desactivar los valores de la matriz durante la iteración de guardar en la memoria?

$myData = array('Key1' => array('value1', 'value2')); 

Pero lo que quiero sería algo así como:

$myData = array([0] => array('MyKey' => array('Key1' => array('value1', 'value2')))); 

así que tomo el primer $myData y darle formato como el segundo $myData. Estoy totalmente de acuerdo con mi algoritmo de formateo. Mi pregunta radica en encontrar una manera de conservar la memoria, ya que estos arreglos pueden ser un poco difíciles de manejar. Por lo tanto, durante mi ciclo foreach copio los valores de la matriz actual (es) en el nuevo formato, luego desarmo el valor con el que estoy trabajando desde la matriz original. Ej .:

$formattedData = array(); 
foreach ($myData as $key => $val) { 
    // do some formatting here, copy to $reformattedVal 

    $formattedData[] = $reformattedVal; 

    unset($myData[$key]); 
} 

es el llamado a unset() una buena idea aquí? Es decir, ¿conserva memoria ya que he copiado los datos y ya no necesito el valor original? O, ¿PHP recolecta basura de forma automática, ya que no hago referencia a ella en ningún código posterior?

El código funciona bien, y hasta ahora mis conjuntos de datos han sido demasiado insignificantes para probar las diferencias de rendimiento. Simplemente no sé si me estoy preparando para errores extraños o hits de CPU más adelante.

Gracias por cualquier idea.
-sR

+0

A menos que sus estructuras de datos sean absolutamente enormes (una gran fracción de su RAM), entonces no se está preocupando por nada. Si php se queda sin mentor, se lo dirá, y puede aumentarlo en php.ini. – Ian

+4

Es una * idea tonta *. Acabas de introducir un efecto secundario que podría olvidarse más tarde por alguna * micro-optimización *: -/Y no, PHP (ni ningún otro lenguaje estándar de GC que yo conozca) es capaz de hacer que los datos * contenidos * en una estructura de datos disponible para reclamación mientras existe una referencia al * contenedor * (esto excluye nociones como referencias blandas/débiles). El 'desarmado' puede/hará que el PHP GC se active, pero el rendimiento real obtenido, si lo hay, debido a la presión de la memoria liberada no es trivial para generalizar. Si esto * se convierte en * un problema, * entonces * diríjalo. –

+0

¿cuál es el tamaño de esta matriz? –

Respuesta

0

A menos que esté accediendo al elemento por referencia, la desactivación no hará absolutamente nada, ya que no puede modificar la matriz durante el iterador.

Dicho esto, generalmente se considera una mala práctica modificar la colección sobre la que se está iterando; un mejor enfoque sería dividir la matriz de origen en fragmentos más pequeños (solo cargando una parte de los datos de origen a la vez) y procese estos, desarmando cada "porción" completa de la matriz sobre la marcha.

+0

"desarmar no hará nada en absoluto" - esto no es correcto, su código desarmará la variable del arreglo original – Andy

+0

@Andy He indicado claramente que no hará nada si ** no se accede por referencia **. Del manual de PHP: "A menos que se haga referencia a la matriz, foreach opera en una copia de la matriz especificada y no la matriz misma." –

+0

Correcto, pero verás que su código está desarmando la variable de la matriz original, no la copia. – Andy

4

Utilice una referencia a la variable en el foreach utilizando el operador &. Esto evita hacer una copia de la matriz en la memoria para foreach para iterar.

edición: como ha señalado Artefacto desarmar la variable sólo disminuye el número de referencias a la variable original, por lo que la memoria almacenada es sólo de punteros en lugar del valor de la variable. Extrañamente el uso de una referencia en realidad aumenta el uso total de la memoria ya que presumiblemente el valor se copia a una nueva ubicación de memoria en lugar de ser referenciada.

A menos que se hace referencia a la matriz, foreach funciona con una copia de la matriz especificada y no la matriz sí. foreach tiene algunos efectos secundarios en el puntero del array. No confíe en el puntero de matriz durante o después del foreach sin restablecerlo.

Utilice memory_get_usage() para identificar la cantidad de memoria que está utilizando.

Hay un buen escrito sobre el uso de la memoria y la asignación here.

Este es un código de prueba útil para ver la asignación de memoria; intente descomentar las líneas comentadas para ver el uso total de la memoria en diferentes escenarios.

echo memory_get_usage() . PHP_EOL; 
$test = $testCopy = array(); 
$i = 0; 
while ($i++ < 100000) { 
    $test[] = $i; 
} 
echo memory_get_usage() . PHP_EOL; 
foreach ($test as $k => $v) { 
//foreach ($test as $k => &$v) { 
    $testCopy[$k] = $v; 
    //unset($test[$k]); 
} 
echo memory_get_usage() . PHP_EOL; 
+0

Gracias por la respuesta y la información útil. Usando su ejemplo de código, estoy viendo un diff de 5MB en uso de memoria cuando uso 'unset()'. Además, el uso de memoria va * arriba * al hacer referencia a la matriz en el foreach (sin utilizar 'unset()'). Interesante ... aunque el tiempo suficiente se haya gastado. – Soulriser

+0

No es realmente correcto, consulte la respuesta de Artefacto a continuación! – GTodorov

2

Si en algún momento en el "formateo" haces algo como:

$reformattedVal['a']['b'] = $myData[$key]; 

A continuación, haciendo unset($myData[$key]); es irrelevante memoria sabia, ya que sólo está disminuyendo la cuenta de referencia de la variable, que ahora existe en dos lugares (dentro de $myData[$key] y $reformattedVal['a']['b']). En realidad, guardas la memoria de indexar la variable dentro de la matriz original, pero eso es casi nada.

+0

Esto no es correcto - por variables predeterminadas no se pasan por referencia, solo los objetos son – Andy

+1

@Andy Primero nadie pasa nada (¿ves alguna función?), segundo, en una asignación de '$ a = $ b' en situaciones normales sin memoria se copia entre las dos variables (PHP implementa copy-on-write), incluso si se comporta como si la memoria hubiera sido copiada. – Artefacto

+0

Mi error, me propuse la asignación en lugar de pasar los parámetros. He agregado código de prueba a mi respuesta para demostrar la memoria guardada usando 'unset()'. – Andy

3

recuerde lo rules of Optimization Club:

  1. La primera regla de la optimización del club es, que no optimizan.
  2. La segunda regla de Optimization Club es que no se optimiza sin medir.
  3. Si su aplicación se ejecuta más rápido que el protocolo de transporte subyacente, la optimización ha terminado.
  4. Un factor a la vez.
  5. No marketroids, no marketroid horarios.
  6. Las pruebas continuarán tanto como sea necesario.
  7. Si esta es su primera noche en el Optimization Club, tiene que escribir un caso de prueba.

Las reglas n. ° 1 y n. ° 2 son especialmente relevantes aquí. A menos que sepa que necesita optimizar y, a menos que haya medido la necesidad de optimizar, no lo haga. Agregar el desarmado agregará un acierto en tiempo de ejecución y hará que los programadores futuros lo hagan.

Déjalo en blanco.

+0

lo que significa # 5 – Jason

+0

"Marketroid" significa alguien del departamento de Marketing. En un sentido más amplio, no permita que alguien no técnico le dicte los términos sobre lo que su programa debería ser capaz de hacer. –

+0

Gracias por la referencia, Andy. Conozco Marketroids demasiado bien. – Soulriser

2

Me estaba quedando sin memoria mientras procesaba líneas de un archivo de texto (xml) dentro de un bucle. Para cualquier persona con una situación similar, esto funcionó para mí:

while($data = array_pop($xml_data)){ 
    //process $data 
} 
Cuestiones relacionadas