Ecco alcune nuove tempistiche:
import contextlib
import timeit
def work_pass():
pass
def work_fail():
1/0
def simple_catch(fn):
try:
fn()
except Exception:
pass
@contextlib.contextmanager
def catch_context():
try:
yield
except Exception:
pass
def with_catch(fn):
with catch_context():
fn()
class ManualCatchContext(object):
def __enter__(self):
pass
def __exit__(self, exc_type, exc_val, exc_tb):
return True
def manual_with_catch(fn):
with ManualCatchContext():
fn()
preinstantiated_manual_catch_context = ManualCatchContext()
def manual_with_catch_cache(fn):
with preinstantiated_manual_catch_context:
fn()
setup = 'from __main__ import simple_catch, work_pass, work_fail, with_catch, manual_with_catch, manual_with_catch_cache'
commands = [
'simple_catch(work_pass)',
'simple_catch(work_fail)',
'with_catch(work_pass)',
'with_catch(work_fail)',
'manual_with_catch(work_pass)',
'manual_with_catch(work_fail)',
'manual_with_catch_cache(work_pass)',
'manual_with_catch_cache(work_fail)',
]
for c in commands:
print c, ': ', timeit.timeit(c, setup)
che ho fatto simple_catch
in realtà chiamare la funzione e ho aggiunto due nuovi parametri di riferimento.
Ecco quello che ho ottenuto:
>>> python2 bench.py
simple_catch(work_pass) : 0.413918972015
simple_catch(work_fail) : 3.16218209267
with_catch(work_pass) : 6.88726496696
with_catch(work_fail) : 11.8109841347
manual_with_catch(work_pass) : 1.60508012772
manual_with_catch(work_fail) : 4.03651213646
manual_with_catch_cache(work_pass) : 1.32663416862
manual_with_catch_cache(work_fail) : 3.82525682449
python2 p.py.py 33.06s user 0.00s system 99% cpu 33.099 total
E per PyPy:
>>> pypy bench.py
simple_catch(work_pass) : 0.0104489326477
simple_catch(work_fail) : 0.0212869644165
with_catch(work_pass) : 0.362847089767
with_catch(work_fail) : 0.400238037109
manual_with_catch(work_pass) : 0.0223228931427
manual_with_catch(work_fail) : 0.0208241939545
manual_with_catch_cache(work_pass) : 0.0138869285583
manual_with_catch_cache(work_fail) : 0.0213649272919
il sovraccarico è molto più piccolo di quanto si rivendicato. Inoltre, l'unico sovraccarico PyPy non sembra essere in grado di rimuovere relativamente allo try
... catch
per la variante manuale è la creazione di un oggetto, che in questo caso viene rimosso banalmente.
Purtroppo with
is way too involved for good optimization by CPython, soprattutto per quanto riguarda contextlib
cui anche PyPy trova difficile da ottimizzare. Questo è normalmente OK perché sebbene la creazione di oggetti + una chiamata di funzione + la creazione di un generatore sia costosa, è economico rispetto a ciò che viene normalmente fatto.
Se si è sicuri che with
sta causando la maggior parte della vostra testa, di convertire i gestori di contesto in istanze memorizzate nella cache come se avessi. Se è ancora troppo sovraccarico, probabilmente avrai un problema più grande con il modo in cui è progettato il tuo sistema. Considera di rendere più ampio lo scope del with
(di solito non è una buona idea, ma è accettabile se necessario).
Inoltre, PyPy. Dat JIT essere veloce.
Non so se la tua affermazione è vera, ma la comunità Python di solito non è interessata alle prestazioni. –
@SiyuanRen "Non so se la tua affermazione è vera" è il motivo per cui ho collegato il codice di benchmarking. È molto facile da gestire. – san
L'ho eseguito e sembra confermare il tuo punto. Ma molte volte i problemi riguardano il benchmarking piuttosto che il sistema attuale, che vedo nel supposto 'C++ è troppo lento su questo e su quello' troppe volte. Non sono molto esperto in Python per raccontare questo. –