2014-01-14 13 views
8

Ich habe überraschend Python Verhalten entdeckt, während Why is reading lines from stdin much slower in C++ than Python? Thread zu untersuchen hatte.Warum Mesh Python Code langsamer als zerlegt?

Wenn ich laufe einfach Python-Code aus diesem Thread

#!/usr/bin/env python 
from __future__ import print_function 
import time 
import sys 


count = 0 
start_time = time.time() 

for line in sys.stdin: 
    count += 1 

delta_sec = time.time() - start_time 
if delta_sec >= 0: 
    lines_per_sec = int(round(count/delta_sec)) 
    print("Read {0:n} lines in {1:.2f} seconds. LPS: {2:n}".format(count, delta_sec, lines_per_sec)) 

es mit Geschwindigkeit 11.5M LPS arbeitet, und wenn ich das ganze Skript in einzelne Funktion

#!/usr/bin/env python 
from __future__ import print_function 
import time 
import sys 


def test(input): 
    count = 0 
    start_time = time.time() 

    for line in input: 
     count += 1 

    delta_sec = time.time() - start_time 
    if delta_sec >= 0: 
     lines_per_sec = int(round(count/delta_sec)) 
     print("Read {0:n} lines in {1:.2f} seconds. LPS: {2:n}".format(count, delta_sec, lines_per_sec)) 


if __name__ == "__main__": 
    test(sys.stdin) 

Code Geschwindigkeiten von bis zu 23M zersetzen LPS.

Warum dieses einfache Refactoring macht meinen Code 2-mal schneller?

Ich habe meine Tests mit python2.7 auf Ubuntu 13.10 laufen.

+0

Einheimische vs Globals – SethMMorton

+1

versuchen 'Eingang = für Zeile in Eingabe sys.stdin: ...' in Ihrem ersten Skript –

+0

diese Methode beschleunigt erste Skript –

Antwort

4

in Bytecode Zusehen hat mir geholfen, diese Frage zu beantworten. Byte Code Teil des ersten Skript für die Arbeit ist:

10   58 SETUP_LOOP    27 (to 88) 
      61 LOAD_NAME    3 (sys) 
      64 LOAD_ATTR    6 (stdin) 
      67 GET_ITER   
     >> 68 FOR_ITER    16 (to 87) 
      71 STORE_NAME    7 (line) 
11   74 LOAD_NAME    4 (count) 
      77 LOAD_CONST    4 (1) 
      80 INPLACE_ADD  
      81 STORE_NAME    4 (count) 
      84 JUMP_ABSOLUTE   68 
     >> 87 POP_BLOCK 

Und Code Byte für einen Teil des zweiten Skript entspricht, ist:

12   18 SETUP_LOOP    24 (to 45) 
      21 LOAD_FAST    0 (input) 
      24 GET_ITER 
     >> 25 FOR_ITER    16 (to 44) 
      28 STORE_FAST    3 (line) 
13   31 LOAD_FAST    1 (count) 
      34 LOAD_CONST    2 (1) 
      37 INPLACE_ADD 
      38 STORE_FAST    1 (count) 
      41 JUMP_ABSOLUTE   25 
     >> 44 POP_BLOCK 

Ich sehe, dass die tatsächliche Differenz zwischen diesen Codes ist LOAD_NAME vs LOAD_FAST und STORE_NAME vs STORE_FAST Opcodes mit. Dokumentation http://docs.python.org/2.7/library/dis.html#opcode-LOAD_FAST sagt, dass LOAD_FAST macht Lookup nur Indizes verwenden, während LOAD_NAME Variable Lookups von string name. Und der erste Ansatz ist zweimal schneller.

+0

Interessant! Was hast du benutzt, um den Bytecode zu zerlegen? – JJC

+1

Standard Python 'Dis' Modul kann fast alle zerlegen :) –

Verwandte Themen