Todo lo que leo sobre mejores prácticas de codificación de PHP sigue diciendo que no lo use require_once
debido a la velocidad.
¿Por qué es esto?
¿Cuál es la forma adecuada / mejor de hacer lo mismo require_once
? Si importa, estoy usando PHP 5.
php
performance
require-once
Uberfuzzy
fuente
fuente
Respuestas:
require_once
yinclude_once
ambos requieren que el sistema mantenga un registro de lo que ya se ha incluido / requerido. Cada*_once
llamada significa verificar ese registro. Así que no hay duda alguna un trabajo extra que se realiza allí, pero lo suficiente para detrimento de la velocidad de toda la aplicación?... Realmente lo dudo ... No, a menos que estés en un hardware realmente viejo o lo estés haciendo mucho .
Si está haciendo miles de
*_once
, podría hacer el trabajo usted mismo de una manera más ligera. Para aplicaciones simples, basta con asegurarse de que solo lo haya incluido una vez , pero si todavía obtiene redefinir errores, podría hacer algo como esto:Personalmente me quedaré con las
*_once
declaraciones, pero en el tonto punto de referencia de millones de pases, puedes ver una diferencia entre los dos:10-100 × más lento
require_once
y es curioso querequire_once
aparentemente sea más lentohhvm
. Nuevamente, esto solo es relevante para su código si está ejecutando*_once
miles de veces.fuente
Este hilo me pone nervioso, porque ya ha habido una "solución publicada", y es, a todos los efectos, incorrecto. Vamos a enumerar:
Las definiciones son realmente caras en PHP. Puede buscarlo o probarlo usted mismo, pero la única forma eficiente de definir una constante global en PHP es a través de una extensión. (Las constantes de clase son en realidad un rendimiento bastante decente, pero este es un punto discutible, debido a 2)
Si está utilizando
require_once()
adecuadamente, es decir, para la inclusión de clases, ni siquiera necesita una definición; solo verifica siclass_exists('Classname')
. Si el archivo que incluye contiene código, es decir, lo está utilizando de manera procesal, no hay absolutamente ninguna razón querequire_once()
sea necesaria para usted; cada vez que incluye el archivo presume que está haciendo una llamada de subrutina.Entonces, por un tiempo, muchas personas usaron el
class_exists()
método para sus inclusiones. No me gusta porque es fugitivo, pero tenían buenas razones para hacerlo:require_once()
era bastante ineficiente antes de algunas de las versiones más recientes de PHP. Pero eso se ha solucionado, y creo que el bytecode adicional que tendría que compilar para el condicional, y la llamada al método adicional, superarían con creces cualquier comprobación interna de tabla hash.Ahora, una admisión: es difícil probar estas cosas, porque representa muy poco tiempo de ejecución.
Aquí está la pregunta en la que debería estar pensando: incluye, como regla general, son caros en PHP, porque cada vez que el intérprete toca uno, debe volver al modo de análisis, generar los códigos de operación y luego retroceder. Si tiene más de 100 incluye, esto definitivamente tendrá un impacto en el rendimiento. La razón por la que usar o no usar require_once es una pregunta tan importante es porque dificulta la vida de los cachés de código de operación. Aquí se puede encontrar una explicación de esto , pero todo se reduce a eso:
Si durante el tiempo de análisis, usted sabe exactamente qué archivos de inclusión necesitará durante toda la vida de la solicitud,
require()
los que están al principio y el caché de código de operación se encargará de todo lo demás por usted.Si no está ejecutando un caché de código de operación, está en un lugar difícil. Incluir todos los elementos incluidos en un archivo (no haga esto durante el desarrollo, solo en la producción) ciertamente puede ayudar a analizar el tiempo, pero es difícil de hacer, y además, necesita saber exactamente qué incluirá durante el solicitud.
La carga automática es muy conveniente, pero lenta, por la razón de que la lógica de carga automática debe ejecutarse cada vez que se realiza una inclusión. En la práctica, descubrí que cargar automáticamente varios archivos especializados para una solicitud no causa demasiado problema, pero no debe cargar automáticamente todos los archivos que necesitará.
Si tiene tal vez 10 incluye (esto es una parte muy atrás del cálculo de la envolvente), todas estas patadas no valen la pena: simplemente optimice las consultas de su base de datos o algo así.
fuente
define()
,require_once()
ydefined()
todos toman alrededor de 1-2 microsegundos cada uno en mi máquina.Sentí curiosidad y revisé el enlace de Adam Backstrom a Tech Your Universe . Este artículo describe una de las razones por las que debe usarse require en lugar de require_once. Sin embargo, sus afirmaciones no fueron válidas para mi análisis. Me interesaría ver dónde podría haber analizado mal la solución. Usé PHP 5.2.0 para las comparaciones.
Comencé creando 100 archivos de encabezado que usaba require_once para incluir otro archivo de encabezado. Cada uno de estos archivos se parecía a:
Los creé usando un truco rápido de Bash:
De esta forma, podría cambiar fácilmente entre require_once y require al incluir los archivos de encabezado. Luego creé un app.php para cargar los cien archivos. Esto se parecía a:
Comparé los encabezados require_once con los encabezados require que usaban un archivo de encabezado similar al siguiente:
No encontré mucha diferencia al ejecutar esto con require vs. require_once. De hecho, mis pruebas iniciales parecían implicar que require_once fue un poco más rápido, pero no necesariamente lo creo. Repetí el experimento con 10000 archivos de entrada. Aquí vi una diferencia constante. Ejecuté la prueba varias veces, los resultados son cercanos, pero el uso de require_once utiliza en promedio 30.8 jiffies de usuario y 72.6 jiffies de sistema; el uso requiere usos en promedio 39.4 jiffies de usuario y 72.0 jiffies del sistema. Por lo tanto, parece que la carga es ligeramente menor usando require_once. Sin embargo, el tiempo del reloj de pared aumenta ligeramente. Las 10,000 llamadas require_once usan 10.15 segundos en promedio y las 10,000 llamadas require usan 9.84 segundos en promedio.
El siguiente paso es analizar estas diferencias. Utilicé strace para analizar las llamadas al sistema que se están realizando.
Antes de abrir un archivo desde require_once se realizan las siguientes llamadas al sistema:
Esto contrasta con requiere:
Tech Your Universe implica que require_once debería hacer más llamadas a lstat64. Sin embargo, ambos realizan la misma cantidad de llamadas lstat64. Posiblemente, la diferencia es que no estoy ejecutando APC para optimizar el código anterior. Sin embargo, a continuación comparé la salida de strace para todas las ejecuciones:
Efectivamente, hay aproximadamente dos llamadas más al sistema por archivo de encabezado cuando se utiliza require_once. Una diferencia es que require_once tiene una llamada adicional a la función time ():
La otra llamada al sistema es getcwd ():
Esto se llama porque decidí la ruta relativa referenciada en los archivos hdrXXX. Si hago de esto una referencia absoluta, entonces la única diferencia es la llamada de tiempo adicional (NULL) realizada en el código:
Esto parece implicar que podría reducir la cantidad de llamadas al sistema utilizando rutas absolutas en lugar de rutas relativas. La única diferencia fuera de eso son las llamadas de tiempo (NULL) que parecen usarse para instrumentar el código para comparar lo que es más rápido.
Otra nota es que el paquete de optimización de APC tiene una opción llamada "apc.include_once_override" que afirma que reduce la cantidad de llamadas al sistema realizadas por require_once e include_once (consulte la documentación de PHP ).
fuente
¿Puede darnos algún enlace a estas prácticas de codificación que diga evitarlo? En lo que a mí respecta, es un completo problema . No he mirado el código fuente yo mismo, pero me imagino que la única diferencia entre
include
yinclude_once
es queinclude_once
agrega ese nombre de archivo a una matriz y verifica la matriz cada vez. Sería fácil mantener esa matriz ordenada, por lo que buscar en ella debería ser O (log n), e incluso una aplicación de tamaño medio solo tendría un par de docenas incluidas.fuente
Una mejor manera de hacer las cosas es usar un enfoque orientado a objetos y usar __autoload () .
fuente
__autoload()
se desaconseja y puede quedar en desuso en el futuro, debería usarspl_autoload_register(...)
estos días ... PS2: no me malinterpreten, a veces uso la funcionalidad de carga automática; )No está usando la función que es mala. Es una comprensión incorrecta de cómo y cuándo usarlo, en una base de código general. Solo agregaré un poco más de contexto a esa noción posiblemente incomprendida:
La gente no debería pensar que require_once es una función lenta. Tienes que incluir tu código de una forma u otra.
require_once()
vs.require()
la velocidad no es el problema. Se trata del rendimiento que obstaculiza las advertencias que pueden resultar por usarlo a ciegas. Si se usa ampliamente sin tener en cuenta el contexto, puede generar una gran pérdida de memoria o un código inútil.Lo que he visto que es realmente malo, es cuando usan enormes marcos monolíticos
require_once()
de todas las maneras incorrectas, especialmente en un entorno complejo orientado a objetos (OO).Tome el ejemplo del uso
require_once()
en la parte superior de cada clase como se ve en muchas bibliotecas:Entonces el
User
clase está diseñada para usar las otras tres clases. ¡Lo suficientemente justo!Pero ahora, ¿qué pasa si un visitante está navegando por el sitio y ni siquiera ha iniciado sesión y se carga el marco?
require_once("includes/user.php");
por cada solicitud individual?Incluye 1 + 3 clases innecesarias que nunca usará durante esa solicitud en particular. Así es como los frameworks hinchados terminan usando 40 MB por solicitud en lugar de 5 MB o menos.
¡La otra forma en que se puede usar mal es cuando una clase es reutilizada por muchos otros! Digamos que tiene alrededor de 50 clases que usan
helper
funciones. Para asegurarse de quehelpers
estén disponibles para esas clases cuando se cargan, obtiene:No hay nada malo aquí per se. Sin embargo, si una solicitud de página incluye 15 clases similares. Estás corriendo
require_once
15 veces, o para una buena visual:El uso de require_once () afecta técnicamente el rendimiento para ejecutar esa función 14 veces, además de tener que analizar esas líneas innecesarias. Con solo otras 10 clases muy utilizadas con ese problema similar, podría dar cuenta de más de 100 líneas de dicho código repetitivo sin sentido.
Con eso, probablemente valga la pena usarlo
require("includes/helpers.php");
en el arranque de su aplicación o marco. Pero como todo es relativo, todo depende de sihelpers
vale la pena ahorrar entre 15 y 100 líneas de la frecuencia de peso versus uso de la claserequire_once()
. Pero si la probabilidad de no usar elhelpers
archivo en una solicitud dada es nula, entoncesrequire
definitivamente debería estar en su clase principal. Tenerrequire_once
en cada clase por separado se convierte en un desperdicio de recursos.La
require_once
función es útil cuando es necesario, pero no debe considerarse como una solución monolítica para usar en todas partes para cargar todas las clases.fuente
El wiki de PEAR2 (cuando existía) solía enumerar buenas razones para abandonar todas las directivas require / include a favor de la carga automática , al menos para el código de la biblioteca. Estos lo atan a estructuras de directorio rígidas cuando modelos de empaque alternativos como phar están en el horizonte.
Actualización: como la versión archivada de la wiki es fea, he copiado las razones más convincentes a continuación:
fuente
Las
*_once()
funciones stat cada directorio principal para garantizar que el archivo que está incluyendo no sea el mismo que el que ya se ha incluido. Esa es parte de la razón de la desaceleración.Recomiendo usar una herramienta como Siege para la evaluación comparativa. Puede probar todas las metodologías sugeridas y comparar los tiempos de respuesta.
Más información
require_once()
está en Tech Your Universe .fuente
Incluso si
require_once
yinclude_once
son más lentos querequire
yinclude
(o cualquier alternativa que pueda existir), estamos hablando del nivel más pequeño de micro-optimización aquí. Dedica mucho más tiempo a optimizar esa consulta de bucle o base de datos mal escrita que preocuparse por algo asírequire_once
.Ahora, uno podría argumentar que
require_once
permite prácticas de codificación deficientes porque no necesita prestar atención para mantener sus inclusiones limpias y organizadas, pero eso no tiene nada que ver con la función en sí misma y especialmente con su velocidad.Obviamente, la carga automática es mejor por la limpieza del código y la facilidad de mantenimiento, pero quiero dejar en claro que esto no tiene nada que ver con la velocidad .
fuente
Usted prueba, usando include, la alternativa de oli y __autoload (); y probarlo con algo como APC instalado.
Dudo que usar constante acelere las cosas.
fuente
Sí, es un poco más caro de lo que se requiere (). Creo que el punto es que si puede mantener su código lo suficientemente organizado como para no duplicar las inclusiones, no use las funciones * _once (), ya que le ahorrará algunos ciclos.
Pero usar las funciones _once () no va a matar su aplicación. Básicamente, simplemente no lo use como una excusa para no tener que organizar sus inclusiones . En algunos casos, su uso sigue siendo inevitable, y no es gran cosa.
fuente
Creo que en la documentación de PEAR, hay una recomendación para require, require_once, include e include_once. Sí sigo esa directriz. Su aplicación sería más clara.
fuente
No tiene nada que ver con la velocidad. Se trata de fallar con gracia.
Si require_once () falla, su script está listo. Nada más se procesa. Si usa include_once (), el resto de su secuencia de comandos intentará continuar renderizando, por lo que sus usuarios podrían no ser más sabios de algo que ha fallado en su secuencia de comandos.
fuente
Mi opinión personal es que el uso de require_once (o include_once) es una mala práctica porque require_once lo comprueba si ya incluyó ese archivo y suprime los errores de archivos doblemente incluidos que resultan en errores fatales (como la declaración duplicada de funciones / clases / etc.) .
Debe saber si necesita incluir un archivo.
fuente