PHP: array_key_exists busca 500 veces más rápido que in_array

En 2014, ya escribieron sobre la búsqueda de la matriz , pero casi nadie entendió.

Desde entonces, muchas versiones de PHP se han lanzado y no se han solucionado, lo que significa que los comentarios son malos y pocas personas lo saben. En python es lo mismo, y en 3 * peor que en 2.7.

A veces necesita encontrar una cadena en una matriz de cadenas , una operación muy frecuente en diferentes algoritmos, y si la matriz es pequeña y se ve un poco y no en un bucle, entonces in_array es normal, no afecta la velocidad general, pero si necesita grandes datos y busca una matriz de mil millones de filas y mil millones de veces , entonces esto es crítico: mejor hora en lugar de semana.

Una prueba simple muestra:

in_array busca ideone.com/Yb1mDa 6600msen 6-9 segundos
y array_key_exists busca lo mismo, pero más rápido que 250 (php5.6 / py3. *) 400+ veces (php7.3 / py2.7) ideone .com / gwSmFc(ciclo aumentado 100 veces) 12 ms (6600/12 = 550 veces + -10% de propagación debido a la carga y la memoria caché)

¿Por qué sucede esto? Considere en detalle:

1) Encontrar cadenas en ensamblador / es puro es ordenar un conjunto de cadenas (rápido o burbuja), luego búsqueda binaria.

El número de pasos en un registro de búsqueda binaria (n) veces depende del tamaño de la matriz y es mucho más pequeño que una búsqueda simple.

Puede ordenar una serie de cadenas por adelantado, una vez y almacenarlas en caché, y luego hacer mil millones de búsquedas. Pero eso no ayuda.

Por defecto, la ordenación ocurre cada vez más, aunque escribieron que mejoraron en 7.2 in_array a través de un hash, pero no mucho.

2) Buscar índice / clave (como una cadena) en la asociación. La matriz / diccionario se produce por hash de cadenas y procesamiento de colisión (errores de búsqueda hash). Un hash es el índice numérico de la matriz y se obtiene como (dirección del elemento cero) + desplazamiento * el tamaño del puntero a la matriz de cadenas con este hash) en 2 pasos. + colisiones de fuerza bruta, pasos en promedio inferiores a la búsqueda binaria.
El hash de índice se realiza automáticamente una vez por adelantado al crear el elemento de diccionario $ m [clave] = val y se almacena en caché.

El tamaño del hash, el algoritmo de hash está cosido en el motor PCP y no se puede cambiar, aunque el código fuente está abierto, puede descargarlo para cambiarlo y compilarlo en su servidor.

No puede leer más, cambie in_array a array_combine + array_key_exists y eso es todo.

El número de pasos cuando se busca por hash depende del número de colisiones y del número de líneas con el mismo hash. Necesitan ser ordenados o también ordenados y búsqueda binaria.

Para reducir las colisiones, puede asignar más memoria, si es posible que ahora no sea un problema como hace 50 años cuando 1 kb de memoria en bobinas magnéticas cuesta como un avión. Y fue entonces cuando se inventaron todos los algoritmos básicos: sort / zip / gif / jpg / etc. - no necesitan mucha memoria, pero son malos, ahora son mucho mejores, pero necesitan mucha memoria 1-16 MB. Sí, hay servidores con 256 MB y cada uno tiene una secuencia separada y 16 MB ya es mucho, pero en el dispositivo del usuario promedio 1 GB al menos y 16 MB es una gota en el cubo.

Puede obtener aún más efecto si reemplaza la llamada a la función array_key_exists con la construcción isset ($ m [clave]), no borra la cola de comandos y la caché, no usa la pila y es más rápido en aproximadamente un 20%.

También puede acelerarlo si crea una matriz de las primeras 2 letras - 4 * 16kb y mira primero en el desplazamiento (índice = código del primer carácter + 2do * 256) un puntero a la matriz hash para el resto de la línea, luego búsquelo entre la pequeña matriz de "colas" de las líneas y las colisiones son mucho más pequeñas.

Requiere aún más memoria y el algoritmo es más complicado, pero la búsqueda es más de 30 veces más rápida. Pero esto no está implementado en php, puede escribir su biblioteca so / dll y llamarla, o pedirle a los desarrolladores que la agreguen en 7.5.

Puede buscar a través de mySQL, pero necesita agrupar las consultas y seguirá siendo más lento.

PD: Este método se encontró accidentalmente al escribir, intuición y experiencia cuando aceleré un sitio web grande y lento, hay muchas sutilezas y trucos, logré exportar los datos de 40 segundos a 0.8 segundos, listas de salida con clasificación y filtros, y muchos otros cosas donde las técnicas estándar, los marcos y los marcos lo hacen muy lentamente, aunque, por supuesto, son convenientes y aceleran el desarrollo.

All Articles