Python 中异常处理程序的成本

发布于 2024-08-26 20:15:04 字数 257 浏览 8 评论 0原文

另一个问题中,接受的答案建议替换(非常便宜)Python 代码中的 if 语句带有 try/ except 块以提高性能。

抛开编码风格问题不谈,假设永远不会触发异常,那么拥有异常处理程序与没有异常处理程序、与使用与零进行比较的 if 语句相比(在性能方面)有多大区别?

In another question, the accepted answer suggested replacing a (very cheap) if statement in Python code with a try/except block to improve performance.

Coding style issues aside, and assuming that the exception is never triggered, how much difference does it make (performance-wise) to have an exception handler, versus not having one, versus having a compare-to-zero if-statement?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

笑叹一世浮沉 2024-09-02 20:15:04

为什么不使用 timeit 模块来测量它?这样您就可以了解它是否与您的应用程序相关。

好的,所以我刚刚尝试了以下操作(在 Windows 11 上使用 Python 3.11.1):

import timeit

statements=["""\
try:
    b = 10/a
except ZeroDivisionError:
    pass""",
"""\
if a:
    b = 10/a""",
"b = 10/a"]

for a in (1,0):
    for s in statements:
        t = timeit.Timer(stmt=s, setup='a={}'.format(a))
        print("a = {}\n{}".format(a,s))
        print("%.2f usec/pass\n" % (1000000 * t.timeit(number=100000)/100000))

结果:

a = 1
try:
    b = 10/a
except ZeroDivisionError:
    pass
0.06 usec/pass

a = 1
if a:
    b = 10/a
0.05 usec/pass

a = 1
b = 10/a
0.03 usec/pass

a = 0
try:
    b = 10/a
except ZeroDivisionError:
    pass
0.27 usec/pass

a = 0
if a:
    b = 10/a
0.02 usec/pass

a = 0
b = 10/a
Traceback (most recent call last):
  File "<stdin>", line 5, in <module>
  File "C:\Python311\Lib\timeit.py", line 178, in timeit
    timing = self.inner(it, self.timer)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<timeit-src>", line 6, in inner
ZeroDivisionError: division by zero

如您所见,使用 try/ except 子句与使用 try/ except 子句之间没有太大区别. 显式的 if 语句,除非触发异常。 (当然,没有任何控制结构是最快的,尽管速度不是很多,而且如果出现任何问题,程序就会崩溃)。

与 2010 年获得的结果进行比较:

a = 1
try:
    b = 10/a
except ZeroDivisionError:
    pass
0.25 usec/pass

a = 1
if a:
    b = 10/a
0.29 usec/pass

a = 1
b = 10/a
0.22 usec/pass

a = 0
try:
    b = 10/a
except ZeroDivisionError:
    pass
0.57 usec/pass

a = 0
if a:
    b = 10/a
0.04 usec/pass

a = 0
b = 10/a
ZeroDivisionError: int division or modulo by zero

我现在使用的 PC 的速度大约是当时的两倍。处理异常的成本看起来是相同的,并且“正常”操作(算术)的改进甚至比控制结构的处理还要多,但多年前的观点仍然成立:

它们都在同一个数量级内,并且无论哪种方式都不太重要。只有当条件确实满足(经常)时,if 版本才会明显更快。

Why don't you measure it using the timeit module? That way you can see whether it's relevant to your application.

OK, so I've just tried the following (using Python 3.11.1 on Windows 11):

import timeit

statements=["""\
try:
    b = 10/a
except ZeroDivisionError:
    pass""",
"""\
if a:
    b = 10/a""",
"b = 10/a"]

for a in (1,0):
    for s in statements:
        t = timeit.Timer(stmt=s, setup='a={}'.format(a))
        print("a = {}\n{}".format(a,s))
        print("%.2f usec/pass\n" % (1000000 * t.timeit(number=100000)/100000))

Result:

a = 1
try:
    b = 10/a
except ZeroDivisionError:
    pass
0.06 usec/pass

a = 1
if a:
    b = 10/a
0.05 usec/pass

a = 1
b = 10/a
0.03 usec/pass

a = 0
try:
    b = 10/a
except ZeroDivisionError:
    pass
0.27 usec/pass

a = 0
if a:
    b = 10/a
0.02 usec/pass

a = 0
b = 10/a
Traceback (most recent call last):
  File "<stdin>", line 5, in <module>
  File "C:\Python311\Lib\timeit.py", line 178, in timeit
    timing = self.inner(it, self.timer)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<timeit-src>", line 6, in inner
ZeroDivisionError: division by zero

As you can see, there is not much of a difference between using a try/except clause vs. an explicit if statement, unless the exception gets triggered. (And of course, not having any control structure is fastest, though not by much, and it will crash the program if anything goes wrong).

Compare this to the results obtained in 2010:

a = 1
try:
    b = 10/a
except ZeroDivisionError:
    pass
0.25 usec/pass

a = 1
if a:
    b = 10/a
0.29 usec/pass

a = 1
b = 10/a
0.22 usec/pass

a = 0
try:
    b = 10/a
except ZeroDivisionError:
    pass
0.57 usec/pass

a = 0
if a:
    b = 10/a
0.04 usec/pass

a = 0
b = 10/a
ZeroDivisionError: int division or modulo by zero

I appears that the PC I'm using now is about twice as fast as the one I had back then. The cost of handling an Exception appears identical, and the "normal" operations (arithmetic) have been improved even more than the handling of control structures, but the point from all those years ago still stands:

It's all within the same order of magnitude and unlikely to matter either way. Only if the condition is actually met (often), then the if version is significantly faster.

绝對不後悔。 2024-09-02 20:15:04

这个问题实际上在设计和历史常见问题解答:

如果没有引发异常,try/ except 块将非常高效。
实际上捕获异常的成本很高。

This question is actually answered in the Design and History FAQ:

A try/except block is extremely efficient if no exceptions are raised.
Actually catching an exception is expensive.

荭秂 2024-09-02 20:15:04

在 Python 3.11 中,

实现了“零成本”异常。当没有引发异常时,try 语句的成本几乎被消除。 (由 Mark Shannon 在 bpo-40222 中贡献。)

https:// docs.python.org/3.11/whatsnew/3.11.html#optimizations

In Python 3.11,

“Zero-cost” exceptions are implemented. The cost of try statements is almost eliminated when no exception is raised. (Contributed by Mark Shannon in bpo-40222.)

https://docs.python.org/3.11/whatsnew/3.11.html#optimizations

み青杉依旧 2024-09-02 20:15:04

这个问题具有误导性。如果您假设异常从不触发,则两者都不是最佳代码。

如果您假设异常是作为错误条件的一部分触发的,那么您已经超出了想要最佳代码的范围(而且您可能不会以这样的细粒度级别处理它)。

如果您使用异常作为标准控制流的一部分 - 这是 Python 式的“请求宽恕,而不是许可”方式 - 那么异常将被触发,并且成本取决于异常的类型,if 的类型,以及您估计异常发生的时间百分比。

This question is misleading. If you assume the exception is never triggered, neither one is optimal code.

If you assume the exception is triggered as part of an error condition, you are already outside the realm of wanting optimal code (and you probably aren't handling it at a fine-grained level like that anyway).

If you are using the exception as part of the standard control flow - which is the Pythonic "ask forgiveness, not permission" way - then the exception is going to be triggered, and the cost depends on the kind of exception, the kind of if, and what percentage of time you estimate the exception happens.

月棠 2024-09-02 20:15:04
问:Python 中的 try/catch 成本高吗?

当我使用 try catch 时我应该担心吗?以什么方式?

这只是已经给出的答案的摘要。

A:出现异常时if要快得多。否则不行。

@SuperNova 写道,异常的成本为零,因此它比没有异常时使用 if 语句更快。然而,处理异常的成本很高,因此:

对可能失败的事情使用 try。如果可能的话,避免尝试你知道会失败的事情

示例:
  1. 好的情况,使用 try:
try:
    x = getdata() # an external function 
except:
    print('failed. Retrying')
  1. 坏的情况,这里首选 if-version:
y = f(x) # f never fails but often returns 0
try:
    z = 1 / y # this fails often
except:
    print('failed.')

# if-version
y = f(x)
if y != 0:
    z = 1 / y
else:
    print('failed.')

Q: Is try/catch costly in python?

Should i be concerned when I use try catch? In what way?

This is just a summary of the answers already given.

A: When there is an exception if is much faster. Otherwise no.

@SuperNova writes that exceptions are at zero cost so it is faster than having an if-statement when no exception. However, handling exceptions is costly so:

Use try for things that can fail. If possible, avoid try for things you know will fail

Example:
  1. Good case, use try:
try:
    x = getdata() # an external function 
except:
    print('failed. Retrying')
  1. Bad case, here if-version is preferred:
y = f(x) # f never fails but often returns 0
try:
    z = 1 / y # this fails often
except:
    print('failed.')

# if-version
y = f(x)
if y != 0:
    z = 1 / y
else:
    print('failed.')

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文