Estaba probando la velocidad de Bash y Python ejecutando un bucle mil millones de veces.
$ cat python.py
#!/bin/python
# python v3.5
i=0;
while i<=1000000000:
i=i+1;
Código bash:
$ cat bash2.sh
#!/bin/bash
# bash v4.3
i=0
while [[ $i -le 1000000000 ]]
do
let i++
done
Usando el time
comando descubrí que el código de Python tarda solo 48 segundos en finalizar, mientras que el código de Bash tardó más de 1 hora antes de que elimine el script.
¿Por qué esto es tan? Esperaba que Bash fuera más rápido. ¿Hay algún problema con mi script o Bash es realmente mucho más lento con este script?
echo echo hello >> $0
y ejecútalo.Respuestas:
Este es un error conocido en bash; vea la página de manual y busque "BUGS":
;)
Para una excelente introducción sobre las diferencias conceptuales entre los scripts de shell y otros lenguajes de programación, recomiendo leer:
Los extractos más pertinentes:
No use bucles grandes en las secuencias de comandos de shell.
fuente
Los bucles de shell son lentos y bash son los más lentos. Los proyectiles no están destinados a realizar trabajos pesados en bucles. Los shells están destinados a lanzar algunos procesos externos optimizados en lotes de datos.
De todos modos, tenía curiosidad por cómo se comparan los bucles de shell, así que hice un pequeño punto de referencia:
( Detalles:
)
Los resultados (abreviados) (tiempo por iteración) son:
De los resultados:
Si desea un bucle de shell un poco más rápido, entonces si tiene la
[[
sintaxis y desea un bucle de shell rápido, está en un shell avanzado y también tiene el bucle de tipo C. Usa el me gusta C para el ciclo, entonces. Pueden ser aproximadamente 2 veces más rápidos que loswhile [
bucles en el mismo shell.for (
ciclo más rápido a aproximadamente 2.7 µs por iteraciónwhile [
bucle más rápido a aproximadamente 5.8 µs por iteraciónC para los bucles puede ser de 3 a 4 órdenes decimales de magnitud más rápido. (Escuché que los Torvalds aman a C).
El C for loop optimizado es 56500 veces más rápido que el
while [
bucle de bash (el bucle de shell más lento) y 6750 veces más rápido que elfor (
bucle de ksh (el bucle de shell más rápido).Una vez más, la lentitud de los shells no debería importar mucho, porque el patrón típico con los shells es descargarse a unos pocos procesos de programas externos optimizados.
Con este patrón, los shells a menudo hacen que sea mucho más fácil escribir scripts con un rendimiento superior a los scripts de Python (la última vez que lo verifiqué, la creación de canales de proceso en Python fue bastante torpe).
Otra cosa a considerar es el tiempo de inicio.
tarda de 30 a 40 ms en mi PC, mientras que los shells tardan alrededor de 3 ms. Si ejecuta una gran cantidad de scripts, esto se suma rápidamente y puede hacer mucho en los 27-37 ms adicionales que Python tarda solo para comenzar. Las secuencias de comandos pequeñas se pueden terminar varias veces en ese período de tiempo.
(NodeJs es probablemente el peor tiempo de ejecución de secuencias de comandos en este departamento, ya que se necesitan unos 100 ms para comenzar (aunque una vez que haya comenzado, sería difícil encontrar un mejor rendimiento entre los lenguajes de secuencias de comandos)).
fuente
ksh88
, AT & Tksh93
,pdksh
,mksh
...) ya que hay un buen montón de variación entre ellos. Parabash
, es posible que desee especificar la versión. Hizo algunos progresos últimamente (eso se aplica también a otros proyectiles).from subprocess import *; p1=Popen(['echo', 'something'], stdout=PIPE); p2 = Popen(['grep', 'pattern'], stdin=p1.stdout, stdout=PIPE); Popen(['wc', '-c'], stdin=PIPE)
. Esto es realmente torpe, pero no debería ser difícil codificar unapipeline
función que hace esto por usted para cualquier número de procesos, lo que resulta enpipeline(['echo', 'something'], ['grep', 'patter'], ['wc', '-c'])
.Hice un poco de prueba, y en mi sistema ejecuté lo siguiente: ninguno hizo el orden de aceleración de magnitud que sería necesario para ser competitivo, pero puede hacerlo más rápido:
Prueba 1: 18.233s
prueba2: 20.45s
prueba3: 17.64s
prueba4: 26.69s
prueba5: 12.79s
La parte importante en este último es la exportación LC_ALL = C. He descubierto que muchas operaciones de bash terminan significativamente más rápido si se usa esto, en particular cualquier función regex. También muestra una sintaxis indocumentada para usar {} y: como un no-op.
fuente
[[
es mucho más rápido que[
. No sabía que LC_ALL = C (por cierto, no necesita exportarlo) hizo la diferencia.[[
es un bash incorporado, y[
realmente/bin/[
es lo mismo que/bin/test
un programa externo. Por eso es más lento.[
está integrado en todos los shells comunes (pruebatype [
). El programa externo está actualmente sin usar.Un shell es eficiente si lo usa para lo que ha sido diseñado (aunque la eficiencia rara vez es lo que busca en un shell).
Un shell es un intérprete de línea de comandos, está diseñado para ejecutar comandos y hacer que cooperen en una tarea.
Si desea contar hasta mil millones, se invoca un (una) de comandos para contar, como
seq
,bc
,awk
opython
/perl
... Correr 1000000000[[...]]
comandos y 1000000000let
comandos está destinada a ser terriblemente ineficiente, especialmente en lobash
que es la capa más lento de todos.En ese sentido, un shell será mucho más rápido:
Aunque, por supuesto, la mayor parte del trabajo se realiza mediante los comandos que invoca el shell, como debería ser.
Ahora, por supuesto, podría hacer lo mismo con
python
:Pero esa no es realmente la forma de hacer las cosas,
python
ya quepython
es principalmente un lenguaje de programación, no un intérprete de línea de comandos.Tenga en cuenta que puede hacer:
¡Pero, en
python
realidad estaría llamando a un shell para interpretar esa línea de comando!fuente
Respuesta: Bash es mucho más lento que Python.
Un pequeño ejemplo es en la publicación de blog Rendimiento de varios idiomas .
fuente
Nada está mal (excepto sus expectativas) ya que Python es bastante rápido para un lenguaje no compilado, consulte https://wiki.python.org/moin/PythonSpeed
fuente
Aparte de los comentarios, puede optimizar un poco el código , por ejemplo
Este código debería tomar un poco menos de tiempo.
Pero obviamente no es lo suficientemente rápido como para ser realmente utilizable.
fuente
He notado una diferencia dramática en bash del uso de expresiones "mientras" y "hasta" lógicamente equivalentes:
No es que realmente tenga una relevancia tremenda para la pregunta, aparte de eso, a veces, las pequeñas diferencias pueden marcar una gran diferencia, a pesar de que esperaríamos que fueran equivalentes.
fuente
((i==900000))
.=
para la asignación. Volverá verdadero inmediatamente. No se realizará ningún bucle.