The impact of Python exceptions on code running performance.
Preface
The exception handling capability of Python is very powerful, but poor use may also bring negative effects. I usually like to use exceptions when writing programs. Although it is better to adopt a defensive encoding method, It will be lazy to handle exceptions. Occasionally, I think about the impact of exception handling on performance, so I tried to test it today.
Python exception (Google open-source Style Guide) tip:
Exceptions are allowed, but be careful.
Definition:
Exception is a way to jump out of the normal control flow of the code block to handle errors or other exception conditions.
Advantages:
The control flow of the normal operation code is not mixed with the error handling code. when a condition occurs, it also allows the control flow to skip multiple frameworks. for example, jump out of N nested functions in one step without executing the wrong code.
Disadvantages:
This may lead to confusing control flow. errors are easily missed when you call a database.
Conclusion:
Exceptions must comply with the following conditions:
An exception is triggered like this:raise MyException("Error message")
Or raise MyException
. Do not use the form of two parameters (raise MyException, "Error message"
) Or an out-of-date string exception (raise "Error message"
).
The module or package should define the Exception base class for its specific domain. This base class should inherit from the built-in Exception class. The Exception base class of the module should be called "Error ".
class Error(Exception): pass
Never useexcept
: To capture all exceptions, do not captureException
OrStandardError
Unless you want to re-trigger this exception, or you are already at the outermost layer of the current thread (remember to print an error message). In terms of exceptions, Python is very tolerant, except
: It will really capture any errors, including Python syntax errors. Use bugs: it is easy to hide real bugs.
Minimize the amount of code in the try/try t block. The larger the size of the try block, the more likely the exception is to be triggered. In this case, try/try t block will hide the real error.
Use the finally clause to execute code that should be executed no matter whether exceptions exist in the try block. This is often useful for clearing resources, such as closing files.
When exceptions are caught, use as instead of commas. For example:
try: raise Errorexcept Error as error: pass
Design Lab Methods
The comparison experiment is simple and intuitive.
First define a decorator to calculate the time required for each function execution:
Def timer (func): import time def wrapper (* args, ** kwargs): startTime = time. time () f = func (* args, ** kwargs) endTime = time. time () passTime = endTime-startTime print "% s used for executing function % f seconds" % (getattr (func, "_ name _"), passTime) return f return wrapper
Then, use the decorator to decorate the test function.
Define another function called do_something. In this function, assign 1 to variable. In each test function, this function is called 1000000 times.
Do_something:
def do_something(): a = 1
I designed different test groups as needed:
Test Group 1 (directly perform time-consuming operations ):
@timerdef test1(): for _ in xrange(1000000): do_something()
Test Group 2 (time-consuming operations are executed in try without throwing an error ):
@timerdef test2(): try: for _ in xrange(1000000): do_something() except Exception: do_something() else: pass finally: pass
Test Group 3 (in the try time-consuming operation, do not throw an error for each try operation ):
@timerdef test3(): for _ in xrange(1000000): try: do_something() except Exception: do_something() else: pass finally: pass
Test Group 4 (during the time-consuming try operation, try each operation and perform Exception Handling (capture specific exceptions thrown )):
@timerdef test4(): zero = 0 for _ in xrange(1000000): try: if zero == 0: raise ZeroDivisionError except ZeroDivisionError: do_something() else: pass finally: pass
Test Group 5 (try time-consuming operations, try each operation and handle exceptions (capture all exceptions try... Failed t BaseException )):
@timerdef test5(): zero = 0 for _ in xrange(1000000): try: if zero == 0: raise ZeroDivisionError except BaseException: do_something() else: pass finally: pass
Test Group 6 (try time-consuming operations, try each operation and handle exceptions (capture all exceptions without any exception types )):
@timerdef test6(): zero = 0 for _ in xrange(1000000): try: if zero == 0: raise ZeroDivisionError except: do_something() else: pass finally: pass
Test Group 7 (time-consuming operations are placed in release T ):
@timerdef test7(): zero = 0 try: if zero == 0: raise ZeroDivisionError except ZeroDivisionError: for _ in xrange(1000000): do_something() else: pass finally: pass
Test Group 8 (defensive encoding ):
@timerdef test8(): zero = 0 for _ in xrange(1000000): if zero == 0: do_something()
Execution result
Comparison conclusion
- By comparing 1 and 2, we can know that the performance consumption is almost the same when the time-consuming operations and time-consuming operations are directly executed in try.
- By comparing 2 and 7, we can know that the performance consumption of abnormal use is almost the same whether it is to put the code in try or in commit T.
- By comparing 2 and 3, we can know that when no error is thrown, the performance consumption of time-consuming try operations is slightly higher than that of time-consuming try operations.
- By comparing 3 and 4, we can know that the performance consumption of no exception throws when using try is almost several times different from that when using try.
- By comparing steps 4 and 5, we can know that during the time-consuming try operation, try each operation and perform Exception Handling (capture the specific exceptions thrown) and try each operation and perform Exception Handling (catch all exceptions try... Except t BaseException) the performance consumption is almost the same.
- By comparing 4 and 8, we can know that the performance consumption by using defensive encoding is almost several times different than that of capturing exceptions.
- By comparing 5 and 6, we can know that all exceptions are caught (try... Faster than capturing all exceptions (try... Except t BaseException.
Summary
- The above comparison conclusions can be summarized:
- The performance consumption is almost the same whether the code is executed in a try or in the retry T.
- The performance consumption of direct code execution is almost the same as that of execution in try without throwing an exception. Of course, in theory, try consumes a little performance and can be ignored.
- Although try... Faster t than try... Except t BaseException and the method for capturing specific exceptions thrown should be slightly faster, but this method is not recommended because the former can easily hide real bugs and cause serious consequences.
- It is usually necessary to capture the specific exceptions thrown rather than all exceptions, although the performance consumption of the two is almost the same.
- Defensive encoding methods consume almost several times the performance compared to capturing exceptions. This programming method should be adopted as much as possible to improve performance and be more reliable.
The above is all about the impact of Python exceptions on code running performance on instance parsing. I hope it will be helpful to you. If you are interested, you can continue to refer to other related topics on this site. If you have any shortcomings, please leave a message. Thank you for your support!