Skip to content

[QUESTION] How to efficiently stop iteration? #2842

Open
@kenoslund-google

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions