Rendimiento de cadena: Python 2.7 frente a Python 3.4 bajo Windows 10 vs. Ubuntu
Caso de uso
Una función simple que verifica si una cadena específica está en otra cadena en una posición que es un múltiplo de 3 (vea aquí para ver unejemplo del mundo real, encontrar codones de parada en una secuencia de ADN).
Las funcionessliding_window
: toma una cadena de longitud 3 y la compara con la cadena de búsqueda; si son idénticos, avanza 3 caracteres.incremental_start
: intenta encontrar la cadena de búsqueda, si la posición encontrada no es múltiplo de 3, intenta encontrar la siguiente posición después de la posición encontrada.
Tenga en cuenta: los datos de muestra son solo para asegurarse de que cada función tiene que pasar por la cadena completa, el rendimiento es similar con datos reales o datos aleatorios.
Resultados
Python 2.7: La inicialsliding_window
la función podría mejorarse en un factor de ~ 39 utilizando la funciónincremental_start
en Python2.7 en Windows 10. Hubo una ligera caída en la mejora del rendimiento en Ubuntu, ~ 34x, ~ 37x, ~ 18x (VM, AWS, nativo), pero aún en el mismo rango.Python 3.4:
sliding_window
se hizo más lento que en Python2.7 (1.8x en Windows, 1.4x y 1.5x en todos los Ubuntus), pero elincremental_start
el rendimiento se redujo en todos los Ubuntus por un factor de 4, 5, 1.7 (VM, AWS, nativo), mientras que apenas cambió en Windows, s.Windows vs UbuntuPython2.7: Ubuntus virtualizado necesitaba menos tiempo para ambas funciones (~ 20-30%), Ubuntu nativo era aproximadamente un 25% más lento para
incremental_start
, mientrassliding_window
fue 40% más rápidoPython3: el
sliding_window
la función necesitaba menos tiempo para finalizar (~ 50%), mientras que la funciónincremental_start
se hizo más lento por un factor de ~ 2-3.Preguntas
¿Qué causa la diferencia de rendimiento en Python 2 vs. Python 3 en Linux vs. Windows?¿Cómo es posible anticipar tal comportamiento y ajustar el código para un rendimiento óptimo?Código
import timeit
text = 'ATG' * 10**6
word = 'TAG'
def sliding_window(text, word):
for pos in range(0, len(text), 3):
if text[pos:pos + 3] == word:
return False
return True
def incremental_start(text, word):
start = 0
while start != -1:
start = text.find(word, start + 1)
if start % 3 == 0:
return False
return True
#sliding window
time = timeit.Timer(lambda: sliding_window(text, word), setup='from __main__ import text, word').timeit(number=10)
print('%3.3f' % time)
#incremental start
time = timeit.Timer(lambda: incremental_start(text, word), setup='from __main__ import text, word').timeit(number=500)
print('%3.3f' % time)
Mesas
Ubuntu vs Windows VM AWS Native
Python2.7-Increment 79% 73% 126%
Python2.7-Sliding 70% 70% 60%
Python3.4-Increment 307% 346% 201%
Python3.4-Sliding 54% 59% 48%
Py2 vs 3 Windows VM AWS Native
Increment 105% 409% 501% 168%
Sliding 184% 143% 155% 147%
Absolute times in seconds
Win10 Ubuntu AWS Native
Py2.7-Increment 1.759 1.391 1.279 2.215
Py2.7-Sliding 1.361 0.955 0.958 0.823
Py3.4-Increment 1.853 5.692 6.406 3.722
Py3.4-Sliding 2.507 1.365 1.482 1.214
Detalles
Windows 10: Windows nativo, 32 bits Python 3.4.3 o 2.7.9, i5-2500, 16 GB de RAM
Máquina virtual Ubuntu: 14.04, se ejecuta en el host de Windows, Python 3.4.3 de 64 bits, Python 2.7.6, 4 núcleos, 4 GB de RAM
AWS: 14.04, microinstancia AWS, 64 bit Python 3.4.3, Python 2.7.6
Ubuntu nativo: 14.04, 64 bit Python 3.4.3, Python 2.7.6, i5-2500, 16 GB de ram [idéntico a la máquina Win10]
Según lo sugerido por Ingazxrange
ybytes
se utilizaron, una ligera mejora en el rendimiento pero una caída masiva en el rendimiento en Ubuntu con Python3.4. El culpable parece serfind
que es mucho más lento cuando se combinan Ubuntu y Py3.4 (lo mismo con Py3.5 que se compiló desde la fuente). Esto parece depender del sabor de Linux, en Debian Py2.7 y Py3.4 se realizó idéntico, en RedHat Py2.7 fue considerablemente más rápido que Py3.4.
Para una mejor comparación, Py3.4 ahora se usa en 64 bits en Windows10 y Ubuntu. Py27 todavía se usa en Win10.
import timeit, sys
if sys.version_info >= (3,0):
from builtins import range as xrange
def sliding_window(text, word):
for pos in range(0, len(text), 3):
if text[pos:pos + 3] == word:
return False
return True
def xsliding_window(text, word):
for pos in xrange(0, len(text), 3):
if text[pos:pos + 3] == word:
return False
return True
def incremental_start(text, word):
start = 0
while start != -1:
start = text.find(word, start + 1)
if start % 3 == 0:
return False
return True
text = 'aaa' * 10**6
word = 'aaA'
byte_text = b'aaa' * 10**6
byte_word = b'aaA'
time = timeit.Timer(lambda: sliding_window(text, word), setup='from __main__ import text, word').timeit(number=10)
print('Sliding, regular: %3.3f' % time)
time = timeit.Timer(lambda: incremental_start(text, word), setup='from __main__ import text, word').timeit(number=500)
print('Incremental, regular: %3.3f' % time)
time = timeit.Timer(lambda: sliding_window(byte_text, byte_word), setup='from __main__ import byte_text, byte_word').timeit(number=10)
print('Sliding, byte string: %3.3f' % time)
time = timeit.Timer(lambda: incremental_start(byte_text, byte_word), setup='from __main__ import byte_text, byte_word').timeit(number=500)
print('Incremental, bytes: %3.3f' % time)
time = timeit.Timer(lambda: xsliding_window(byte_text, byte_word), setup='from __main__ import byte_text, byte_word').timeit(number=10)
print('Sliding, xrange&bytes: %3.3f' % time)
time = timeit.Timer(lambda: text.find(word), setup='from __main__ import text, word').timeit(number=1000)
print('simple find in string: %3.3f' % time)
Win10-py27 Wi10-py35 VM-py27 VM-py34
1.440 2.674 0.993 1.368
1.864 1.425 1.436 5.711
1.439 2.388 1.048 1.219
1.887 1.405 1.429 5.750
1.332 2.356 0.772 1.224
3.756 2.811 2.818 11.361