[Mlir-commits] [mlir] [MLIR][Python] Don't throw in PyDenseArrayIterator dunderNext (PR #218193)
Maksim Levental
llvmlistbot at llvm.org
Sat Aug 22 21:49:14 PDT 2026
https://github.com/makslevental created https://github.com/llvm/llvm-project/pull/218193
`PyDenseArrayIterator::dunderNext` signaled iterator exhaustion by throwing `nanobind::stop_iteration()`. Raising a C++ exception to signal `StopIteration` incurs stack-unwinding cost on every loop over a dense array attribute.
#175377 replaced this pattern with `PyErr_SetNone(PyExc_StopIteration)` (return a null object after setting the Python error indicator) for the other iterators in the bindings, measuring a ~14% improvement on a container-walk benchmark. `PyDenseArrayIterator` was missed in that change and still throws.
This PR applies the same conversion:
- Signal exhaustion via `PyErr_SetNone(PyExc_StopIteration)` and return `nanobind::object()`.
- Return type changes from `EltTy` to `nanobind::typed<nanobind::object, EltTy>` so the method can return a null object after setting the error, matching the existing `nanobind::typed<nanobind::object, PyAttribute>` iterator in the same header.
Standalone perf/consistency fix; independent of any nanobind version bump.
>From 232b40cec9c5208e4f06e4815d84269806038837 Mon Sep 17 00:00:00 2001
From: makslevental <maksim.levental at gmail.com>
Date: Sat, 22 Aug 2026 21:47:07 -0700
Subject: [PATCH] [MLIR][Python] Don't throw in PyDenseArrayIterator dunderNext
Signal iterator exhaustion via PyErr_SetNone(PyExc_StopIteration) instead
of throwing nanobind::stop_iteration(), matching the pattern used by the
other iterators in the Python bindings (see #175377). Raising a C++
exception to signal StopIteration incurs stack-unwinding cost on every
loop, which PyDenseArrayIterator was still paying. The return type changes
to nanobind::typed<nanobind::object, EltTy> so the method can return a null
object after setting the Python error indicator.
---
mlir/include/mlir/Bindings/Python/IRAttributes.h | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/mlir/include/mlir/Bindings/Python/IRAttributes.h b/mlir/include/mlir/Bindings/Python/IRAttributes.h
index fcc003ab365fa..fbebea40f1ac8 100644
--- a/mlir/include/mlir/Bindings/Python/IRAttributes.h
+++ b/mlir/include/mlir/Bindings/Python/IRAttributes.h
@@ -131,11 +131,16 @@ class MLIR_PYTHON_API_EXPORTED PyDenseArrayAttribute
PyDenseArrayIterator dunderIter() { return *this; }
/// Return the next element.
- EltTy dunderNext() {
- // Throw if the index has reached the end.
- if (nextIndex >= mlirDenseArrayGetNumElements(attr.get()))
- throw nanobind::stop_iteration();
- return DerivedT::getElement(attr.get(), nextIndex++);
+ nanobind::typed<nanobind::object, EltTy> dunderNext() {
+ // Set StopIteration if the index has reached the end. Signaling
+ // exhaustion via the Python error indicator rather than a C++ exception
+ // avoids the cost of stack unwinding on every iteration.
+ if (nextIndex >= mlirDenseArrayGetNumElements(attr.get())) {
+ PyErr_SetNone(PyExc_StopIteration);
+ // python functions should return NULL after setting any exception
+ return nanobind::object();
+ }
+ return nanobind::cast(DerivedT::getElement(attr.get(), nextIndex++));
}
/// Bind the iterator class.
More information about the Mlir-commits
mailing list