Live data from Hacker News

Python 3.11: “Zero cost” exception handling

bugs.python.org

41–50 of 96 posts

Re: Python 3.11: “Zero cost” exception handling

#41

Earlier quoted context omitted.

This covers it very well: https://devblogs.microsoft.com/python/idiomatic-python-eafp-... In particular, this is not idiomatic python: if "key" in dict_: value += dict_["key"] But this is: try: value += dict_["key"] except KeyError: pass I too hate using the exception handling in this way, and if you aren't careful, you end up papering over other unexpected exceptions in your code, so you have to be (A) very specific…

If you don't care about a key existing, then this works. do_something(dict_.get("key", None)) I use that a lot for data parsing. Passing the exception is not very clean IMHO. I stick to d[key] nomenclature when I need assurance that all the keys are present in the dictionary, and .get(key,None) when I don't.

.get("key") is enough, as the default is already None.

And if you care about the default value being a particular type, when there may also be None in the input stream, do something like:

x.get("key") or []

or

str(x.get("key") or "") # Guarantee strings and avoid "None"!

Re: Python 3.11: “Zero cost” exception handling

#42

Earlier quoted context omitted.

Zero cost refers to the cost when no exception is thrown , not the overhead of exceptions. It may be more expensive throwing an exception under "zero cost" exception model, as throwing an exception may require parsing some data in the executable. (I'm not sure about the implementation, so this is just a may...)

> Zero cost refers to the cost when no exception is thrown, not the overhead of exceptions There are recent benchmarks where .NET "zero cost when an try-catch block is present and exception not thrown" turned out to be significantly slower then the alternative without such a block. It turns out that the try-catch block is a barrier across which some optimisations and re-organisations (e.g. method inlining) cannot hap…

Makes sense. Basically it’s zero-cost for exceptions-unaware code. I don’t know if it’s been changed with more modern jits but used to be chrome was unable to jit functions with try statements (similar to functions using `arguments` or `with` I think).

Re: Python 3.11: “Zero cost” exception handling

#43
post #3

Earlier quoted context omitted.

> I've felt weird using exceptions like that How should they be used instead? Maybe I don't understand what you mean by "idiomatic control flow uses exceptions" - could you give an example. Maybe there is some use of exceptions that I'm not quite familiar with in Python.

This covers it very well: https://devblogs.microsoft.com/python/idiomatic-python-eafp-... In particular, this is not idiomatic python: if "key" in dict_: value += dict_["key"] But this is: try: value += dict_["key"] except KeyError: pass I too hate using the exception handling in this way, and if you aren't careful, you end up papering over other unexpected exceptions in your code, so you have to be (A) very specific…

If I find if's and try's looking ugly for a particular use case I try to figure out how to get rid of them. For your first example I would do this, assuming value is a number.

  value += dict_.get('key',0)
Though I agree with using an if in the second example, if there isn't a better way to do the iteration to avoid looking up keys that don't exist.

Re: Python 3.11: “Zero cost” exception handling

#44
post #32
post #22

Earlier quoted context omitted.

Exceptions always have a cost. What happened with many C++ runtimes is that they moved the execution cost of exceptions almost entirely into the exception raising mechanism so that the path of execution that does not raise exceptions has no overhead due to exceptions. This was not the case with the Python runtime. The "zero cost" implementation for C++ exceptions in the case of ELF binaries means storing a bunch of s…

One of the stated benefits of this is > Calls to Python functions would be faster as frame objects would be considerably smaller. Currently each frame carries 240 bytes of overhead for exception handling. I guess that’s where this will pay even in a language/ecosystem where exceptions aren’t exceptional.

It's a time-space tradeoff. A few bytes less overhead on the stack, a few CPU cycles more overhead when raising an exception. Is it a net win? Show me empirical test results.

Re: Python 3.11: “Zero cost” exception handling

#45
post #22
post #2

They weren't zero cost before? In a language where idiomatic control flow uses exceptions? That's crazy! I've felt weird using exceptions like that but I always assumed that CPython was optimized to minimize overhead of exceptions and exception handlers.

Exceptions always have a cost. What happened with many C++ runtimes is that they moved the execution cost of exceptions almost entirely into the exception raising mechanism so that the path of execution that does not raise exceptions has no overhead due to exceptions. This was not the case with the Python runtime. The "zero cost" implementation for C++ exceptions in the case of ELF binaries means storing a bunch of s…

> exceptions are a fundamental control-flow mechanism in Python

Where do you have this from?

In almost all Python code I have seen, exceptions are still for, as the name says, exceptions. So in the common cases, there would be no exceptions raised. I would assume that the exception handling code (under `except ...`) will be run only a tiny fraction of times compared to the other code, at least in most cases.

I would argue, if one abuses exceptions for any sort of control flow logic, this is bad design.

See also the list of builtin exceptions: https://docs.python.org/3/library/exceptions.html

From those, yes, there is StopIteration and StopAsyncIteration, which are used for control-flow, but the handling of those is anyway internal in CPython, and so the discussion about zero cost does not apply, as it would not change this (as far as I understand the current proposal).

Otherwise, all other exceptions are not used for control-flow.

Re: Python 3.11: “Zero cost” exception handling

#46
post #22

Earlier quoted context omitted.

Exceptions always have a cost. What happened with many C++ runtimes is that they moved the execution cost of exceptions almost entirely into the exception raising mechanism so that the path of execution that does not raise exceptions has no overhead due to exceptions. This was not the case with the Python runtime. The "zero cost" implementation for C++ exceptions in the case of ELF binaries means storing a bunch of s…

> exceptions are a fundamental control-flow mechanism in Python Where do you have this from? In almost all Python code I have seen, exceptions are still for, as the name says, exceptions. So in the common cases, there would be no exceptions raised. I would assume that the exception handling code (under `except ...`) will be run only a tiny fraction of times compared to the other code, at least in most cases. I would…

The for loop in python is a try/except catching StopIteration in a trench coat.

Also, EAFP, and the context manager protocol.

Re: Python 3.11: “Zero cost” exception handling

#47
Wow! Prior to reading this, I was not aware of "Zero Cost" exception handling. While I am only a Python developer, I always assumed that in any programming language, exception handling, regardless of whether an exception is raised or not, cost some CPU cycles. I work at an HFT firm and they test their changes in equations in Python programs on crypto rather than C++. So I resorted to using try-except blocks in Python to reduce "branching" i.e if-elif-else blocks. I would just add all the different conditional functions in a dictionary and manage calls based on keys and handle exceptions. I don't know if that's the best way to improve speed, but I would like to check if this has any impact on it.

Re: Python 3.11: “Zero cost” exception handling

#48

Wow! Prior to reading this, I was not aware of "Zero Cost" exception handling. While I am only a Python developer, I always assumed that in any programming language, exception handling, regardless of whether an exception is raised or not, cost some CPU cycles. I work at an HFT firm and they test their changes in equations in Python programs on crypto rather than C++. So I resorted to using try-except blocks in Python…

There are some weird performance optimizations in Python, e.g.,

    item = some_dict.get(key)
    if item is None:
        # key does not exist
Versus

    try:
        item = some_dict[key]
    except KeyError:
        # key does not exist
When I tested these (admittedly, a while ago), which one was faster depended on how often the key was missing. If “missing key” was an expected case, the first one was faster. If “missing key” was uncommon, the second was faster. It sounds like the fast path in the second case is getting faster, so this performance gap may be increasing.

Re: Python 3.11: “Zero cost” exception handling

#49

Wow! Prior to reading this, I was not aware of "Zero Cost" exception handling. While I am only a Python developer, I always assumed that in any programming language, exception handling, regardless of whether an exception is raised or not, cost some CPU cycles. I work at an HFT firm and they test their changes in equations in Python programs on crypto rather than C++. So I resorted to using try-except blocks in Python…

You may enjoy programming in Elixir if you like that style. In Elixir, you only program the “happy path” and just let things fail. Then you rely on supervisor processes to handle the exceptions/errors. Well, at least that is the idea. I think people still do tests and function guards and things. but the “let it fail” idea is definitely part of the Erlang/Elixir world.

The sad thing is that there really isn’t any “learn elixir” book that teaches this idiomatic design. A student of Elixir should set up an umbrella application from the very first hello world, in my opinion.

Re: Python 3.11: “Zero cost” exception handling

#50
post #3

Earlier quoted context omitted.

> I've felt weird using exceptions like that How should they be used instead? Maybe I don't understand what you mean by "idiomatic control flow uses exceptions" - could you give an example. Maybe there is some use of exceptions that I'm not quite familiar with in Python.

When using a for-loop over an iterator, the iterator protocol in Python says to keep returning elements until you run out, at which point you throw an exception. So every loop over an iterator or iterable object in python throws an exception when it is done. https://docs.python.org/3/library/stdtypes.html#iterator.__n...

While that's true at the Python language level, there are already special optimizations for this in CPython: `tp_iternext` is not required to set an exception. If it returns NULL without setting an exception, that's taken to be the end of iteration.

If you call `next()` in Python, this special case is translated to a `StopIteration` exception. But if you use a for-loop, it can directly stop iterating without ever materializing the `StopIteration` exception. So the overhead of Python raise/try-except is already irrelevant for the for-loop.

Post reply on HN