2012-07-28 30 views
11

Imagine que tiene la siguiente matriz de enteros:PHP y el bebé millones de gama

array(1, 2, 1, 0, 0, 1, 2, 4, 3, 2, [...]); 

Los enteros ir en un máximo de un millón de entradas; solo que, en lugar de estar codificados, se generaron previamente y se almacenaron en un archivo con formato JSON (de aproximadamente 2 MB de tamaño). El orden de estos enteros importa, no puedo generarlo aleatoriamente cada vez porque debe ser coherente y tener siempre los mismos valores en los mismos índices.

Si este archivo se vuelve a leer en PHP después (por ejemplo, utilizando file_get_contents + json_decode) que se necesita de 700 a 900ms sólo para obtener la matriz de vuelta - "Está bien", pensé, "es probable que sea razonable, ya que json_decode tiene que analizar alrededor de 2 millones de caracteres, vamos a caché ". APC lo almacena en caché en una entrada que toma aproximadamente 68 MB, probablemente sea normal, los zvales son grandes. Recuperando sin embargo, esta matriz de APC también toma unos buenos 600 ms, lo cual está en mis ojos todavía demasiado.

Editar: APC realiza una serialización/deserialización para almacenar y recuperar contenido que con una matriz de millones de elementos es un proceso largo y pesado.

Así que las preguntas:

  • debo esperar esta latencia si tengo la intención de cargar una matriz de un millón de entradas, sin importar el almacén de datos o el método, en PHP? Por lo que yo entiendo APC almacena la zval sí mismo, lo que en teoría recuperándolo de APC debe ser tan rápido como pueda conseguir (sin análisis, sin conversión, hay acceso al disco)

  • ¿Por qué es APC tan lento para algo tan aparentemente simple?

  • ¿Hay alguna manera eficiente de cargar una matriz de un millón de entradas completamente en memoria usando PHP? suponiendo que el uso de RAM no sea un problema.

  • Si tuviera acceso solo a las porciones de esta matriz basadas en índices (por ejemplo, cargando el fragmento del índice 15 al índice 76) y nunca tengo la matriz completa en la memoria (sí, entiendo que esta es la manera correcta de hacerlo, pero quería saber todos los lados), ¿cuál sería el sistema de almacenamiento de datos más eficiente para la matriz completa? Obviamente no es un RDBM; Estoy pensando en redis, pero me gustaría escuchar otras ideas.

+1

¿Has probado [SplFixedArray] (http://php.net/manual/en/class.splfixedarray.php)? – Buddy

+0

@Buddy yup, no mucha diferencia, probablemente use menos memoria pero APC tarda igualmente. – Mahn

+1

Si los números son pequeños y la matriz es estática, ¿no puede usar un solo objeto de cadena de 1Mb? – 6502

Respuesta

3

Supongamos que los números enteros son todos 0-15. A continuación, puede almacenar 2 por byte:

<?php 
$data = ''; 
for ($i = 0; $i < 500000; ++$i) 
    $data .= chr(mt_rand(0, 255)); 

echo serialize($data); 

Para ejecutar: php ints.php > ints.ser

Ahora usted tiene un archivo con una cadena de bytes que contiene 500.000 1.000.000 enteros aleatorios de 0 a 15.

Para la carga:

<?php 
$data = unserialize(file_get_contents('ints.ser')); 

function get_data_at($data, $i) 
{ 
    $data = ord($data[$i >> 1]); 

    return ($i & 1) ? $data & 0xf : $data >> 4; 
} 

for ($i = 0; $i < 1000; ++$i) 
    echo get_data_at($data, $i), "\n"; 

El tiempo de carga en mi máquina es de aproximadamente .002 segundos.

Por supuesto, esto podría no ser directamente aplicable a su situación, pero será mucho más rápido que una matriz de PHP hinchada de un millón de entradas. Francamente, tener una matriz tan grande en PHP nunca es la solución adecuada.

No estoy diciendo que esta sea la solución adecuada tampoco, pero definitivamente es factible si se ajusta a sus parámetros.

Tenga en cuenta que si su matriz tuviera números enteros en el rango 0-255, podría deshacerse del embalaje y simplemente acceder a los datos como ord($data[$i]). En ese caso, su cadena tendría 1M de longitud.

Finalmente, de acuerdo con la documentación de file_get_contents(), php mapeará la memoria del archivo.Si es así, su mejor rendimiento sería volcar bytes sin formato a un archivo, y lo utilizan como:

$ints = file_get_contents('ints.raw'); 
echo ord($ints[25]); 

Esto asume que ints.raw es exactamente un millón de bytes de longitud.

+0

Matthew, gracias por la versión a mano de mi sugerencia. Un par de puntos: Sí 'f_g_s()' utiliza mmap en el archivo en la mayoría de los casos (p. Ej., No para el archivo montado de NFS), pero aún así copia los contenidos en una cadena localmente asignada. Sí, el acceso 'ord ($ ints [NNN])' genera la secuencia de código de operación más eficiente. Su 2mS se debe a que el contenido del archivo está en caché VFAT. Este puede no ser el caso en un servidor de producción. – TerryE

+0

Esto es básicamente lo que imaginé cuando 6502 y TerryE lo sugirieron, pero es genial verlo escrito de todos modos. Esos 2ms es probablemente porque el archivo fue almacenado en la memoria caché como menciones de @TerryE, pero hay una muy buena posibilidad de que se pueda almacenar en caché en APC sin problemas y sin sobrecargar la carga. Lo echaré un vistazo. – Mahn

+0

De hecho, almacenar y recuperar esto de APC se puede hacer en un abrir y cerrar de ojos. Voy a aceptar esta respuesta porque el rendimiento y la memoria es lo mejor que se puede obtener si se va a almacenar toda la matriz en la memoria; si debo o no almacenar todo en la memoria es otra cuestión sobre la que aún tengo que decidir, pero mientras tanto, esto hará un mejor trabajo. – Mahn

2

APC almacena los datos serializados, por lo que debe ser deserializado, ya que se carga desde APC. Ahí es donde está tu sobrecarga.

La manera más eficiente de cargarlo es escribir en un archivo como PHP e incluir(), pero nunca tendrá ningún nivel de eficiencia con una matriz que contiene un millón de elementos ... toma una cantidad enorme de memoria, y lleva tiempo cargar. Esta es la razón por la que se inventaron las bases de datos, entonces, ¿cuál es su problema con una base de datos?

EDITAR

Si desea acelerar serializar/deserializar, echar un vistazo a la extensión igbinary

+1

Es posible que nunca haya oído hablar de eso. –

+0

Sí, nunca escuché sobre una base de datos :) Nada en contra de las bases de datos, simplemente pensé que dado que los datos están destinados a ser algo estático algo estático que vive en la memoria como que APC haría un mejor trabajo; es una pena saber que APC serializa los datos, pensé que ese no era el caso. – Mahn

+0

Encontrará que la mayoría de las memorias caché (APC, memcache, redis, etc.) necesitarán serializar los datos porque están diseñados como herramientas multiplataforma, por lo que no están diseñados específicamente para los tipos de datos PHP/zvals. –

1

no puedo generar de forma aleatoria cada vez, ya que debe ser coherente y siempre tener los mismos valores en los mismos índices.

¿Alguna vez ha leído números pseudoaleatorios? Hay una pequeña cosa llamada semilla que aborda este problema.

También compare sus opciones y afirmaciones. ¿Ha cronometrado el archivo_get_contents vs. el json_decode? Aquí hay una compensación entre los costos de almacenamiento y acceso. P.ej. si sus números son 0..9 (o 0..255), entonces puede ser más fácil almacenarlos en una cadena de 2Mb y usar una función de acceso en esto. 2Mb cargará más rápido ya sea desde el FS o APC.

+0

Sí, generar algorítmicamente lo mismo la lista de enteros basados ​​en una semilla fija es una de las opciones que estaba pensando, y posiblemente la más elegante; Examinaré los números pseudoaleatorios y veré si pueden encajar en lo que necesito. – Mahn

1

Como dijo Mark, esta es la razón por la que se crearon las bases de datos, que le permiten buscar (y manipular, pero no necesitar) datos basados ​​en sus patrones de uso habituales. También puede ser más rápido que implementar tu propia búsqueda usando la matriz. Supongo que estamos hablando de cerca de 2-300MB de datos (antes de la serialización) que se serializan y deserializan cada vez que se accede a la matriz.

Si desea acelerarlo, intente asignar cada elemento de la matriz por separado; puede cambiar la sobrecarga de llamada de función por el tiempo empleado en la serialización. También podría extender esto con su propia extensión, envolviendo su conjunto de datos en una pequeña interfaz de recuperación.

Supongo que la razón por la que no puede almacenar directamente los zvals es porque contienen un estado interno, y simplemente no puede apuntar la tabla de símbolos de la variable a la tabla anterior.