2015-01-06 10 views
7

Sto esaminando gli esempi citati here e sto guardando this example. Ho eseguito un esempio di esempio su ipython, e il risultato è coerente, cioè, "%d" è più lento di "%s":Perché% s è più veloce di% d per la sostituzione di interi in python?

In [1]: def m1(): 
    ...:  return "%d" % (2*3/5) 

In [2]: def m2(): 
    ...:  return "%s" % (2*3/5) 

In [4]: %timeit m1() 
1000000 loops, best of 3: 529 ns per loop 

In [5]: %timeit m2() 
1000000 loops, best of 3: 192 ns per loop 

In [6]: from dis import dis 

In [7]: dis(m1) 
    2   0 LOAD_CONST    1 ('%d') 
       3 LOAD_CONST    5 (6) 
       6 LOAD_CONST    4 (5) 
       9 BINARY_DIVIDE  
      10 BINARY_MODULO  
      11 RETURN_VALUE   

In [9]: dis(m2) 
    2   0 LOAD_CONST    1 ('%s') 
       3 LOAD_CONST    5 (6) 
       6 LOAD_CONST    4 (5) 
       9 BINARY_DIVIDE  
      10 BINARY_MODULO  
      11 RETURN_VALUE   

Entrambi i blocchi di codice sono simili, e anche l'uscita del disassembler è lo stesso, quindi perché è "%s" più veloce di "%d"?

+0

Deve ovviamente essere l'implementazione in sovraccarico BINARY_MODULO, non che questa sia una spiegazione ;-) Senz'altro .. quando Python ha iniziato a eseguire il piegamento costante ('2 * 3')? – thebjorn

+0

Non sono sicuro del perché la differenza ci sia, ma ciò che sta facendo la differenza nel tempo non sta accadendo nel bytecode python, ma nell'implementazione dell'operatore del modulo, che probabilmente accade in C. Se non sto sbagliando, quello è perché non si presenta nello smontaggio. – bigblind

+2

Solo una nota: la differenza tra i due avviene all'interno dell'operatore modulo, quindi non apparirà nel bytecode della funzione che utilizza quell'operatore. – BrenBarn

risposta

5

Questo è stato discusso in hacker news, io sono solo la formattazione risposta @nikital per SO:

La funzione PyString_Format nel Objects/stringobject.c fa la formattazione per l'operatore %. Per %s chiama _PyObject_Str che a sua volta chiama str() sull'oggetto. Per %d chiama formatint (che si trova nello stesso file).

L'implementazione str() per interi è in int_to_decimal_string in (Objects/intobject.c) ed è incredibilmente semplice:

do { 
    *--p = '0' + (char)(absn % 10); 
    absn /= 10; 
} while (absn); 

Il codice per formatint è il modo più complesso, e contiene due chiamata alla snprintf origini:

PyOS_snprintf(fmt, sizeof(fmt), "%s%%%s.%dl%c", 
       sign, (flags&F_ALT) ? "#" : "", 
       prec, type); 
// ... 
PyOS_snprintf(buf, buflen, fmt, -x); 

Il numero nativo snprintf è più pesante perché gestisce precisione, zero padding e cose del genere.

Credo che questo sia il motivo per cui %d è più lento. %s è un ciclo "divide per 10 e sottrarre" mentre %d sono due chiamate di libreria al numero intero sprintf. Tuttavia in realtà non ho profilato il codice perché non ho una build di debug, quindi potrei sbagliarmi completamente.

+0

Ahhhh! Ho trovato il link per il sito web nella newsletter di notizie sugli hacker oggi, dovrebbe averlo visto anche nei forum. Comunque, grazie e +1. –