Oto niektóre nowe czasy:
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)
Zrobiłem simple_catch
rzeczywiście wywołać funkcję i Dodałem dwa nowe punkty odniesienia.
Oto co mam:
>>> 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
I 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
koszt jest znacznie mniejszy, niż twierdzi. Co więcej, jedyny napowietrzny PyPy nie wydaje się być w stanie usunąć w stosunku do try
... catch
dla wariantu ręcznego to tworzenie obiektu, który w tym przypadku jest trywialnie usuwany.
Niestety with
is way too involved for good optimization by CPython, zwłaszcza w odniesieniu do contextlib
który nawet pypy znajdzie trudno zoptymalizować. Zwykle jest tak, ponieważ chociaż tworzenie obiektu + wywołanie funkcji + tworzenie generatora jest kosztowne, to jest ono w porównaniu do tego, co zwykle wykonuje się.
Jeśli jesteś pewnie, który powoduje, że with
powoduje większość kosztów, przekonwertuj menedżerów kontekstów do pamięci podręcznej instancji, tak jak ja. Jeśli nadal jest to zbyt duże obciążenie, prawdopodobnie masz większy problem z zaprojektowaniem systemu. Zastanów się nad zwiększeniem zakresu (zwykle nie jest to dobry pomysł, ale akceptowalny, jeśli zajdzie taka potrzeba).
Również PyPy. Dat JIT be fast.
Nie wiem, czy twoje oświadczenie jest prawdziwe, ale społeczność Pythona generalnie nie jest zainteresowana wydajnością. –
@SiyuanRen "Nie wiem, czy twoje stwierdzenie jest prawdziwe", dlatego połączyłem kod testu porównawczego. Jest bardzo łatwy do uruchomienia. – san
Uruchomiłem to i wydaje się, że to potwierdza twój punkt widzenia. Ale wiele razy problemy są z benchmarkingiem, a nie z rzeczywistym systemem, który widzę w rzekomym "C++ jest zbyt wolny na to i to" zbyt wiele razy. Nie jestem tak dobrze zorientowany w Pythonie, żeby powiedzieć w tym. –