Skip to content

[QUESTION] How to efficiently stop iteration? #2842

Description

@kenoslund-google

I defined an iterable class in pybind11 by creating a __getitem__ method which throws a std::out_of_range exception if the index is out of range. While this successfully stops the iteration in python (without raising a python exception), it appears that stopping the iteration takes about 100us vs 1us for a native implementation, so ~100x slower. I tried the following things:

A. PyErr_SetString(PyExc_StopIteration, "..."); throw error_already_set();: takes ~25us, so ~4x faster than std::out_of_range but still ~25x slower than the native version.
B. PyErr_SetString(PyExc_StopIteration, "..."); and then propagating the error manually, without throwing a C++ exception. This takes ~5us, so ~20x faster than std::out_of_range and "only" ~5x slower than the native version.

However, option B requires returning a NULL PyObject to python, which the dispatcher doesn't like- it triggers the error here: https://github.com/pybind/pybind11/blob/master/include/pybind11/pybind11.h#L924-L928. In order to run option 2, I have to patch those lines to check PyErr_Occurred() and only set an error if none is already set.

so, my questions:

  1. Is there any equally or more efficient way to define an iterable object in C++ using pybind11 than __getitem__ and option B above?
  2. If the answer to that is "no", can cpp_function::dispatcher be patched to allow setting a python error directly, without throwing a C++ exception?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions