GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-139888: Add PyTupleWriter C API (stack flavor) by vstinner · Pull Request #140129 · python/cpython

Conversation

@vstinner

@vstinner vstinner commented Oct 14, 2025 •
edited by github-actions Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member
  • Add _PyTuple_NewNoTrack() and _PyTuple_ResizeNoTrack() helper functions.
  • Modify PySequence_Tuple() to use PyTupleWriter API.
  • Soft deprecate _PyTuple_Resize().
  • Issue: [C API] Add PyTupleWriter API #139888

📚 Documentation preview 📚: https://cpython-previews--140129.org.readthedocs.build/

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@bedevere-app bedevere-app Bot mentioned this pull request Oct 14, 2025
[C API] Add PyTupleWriter API #139888
Closed
@vstinner
pythongh-139888: Add PyTupleWriter C API (stack flavor)
e5a4e4e
* Add _PyTuple_NewNoTrack() and _PyTuple_ResizeNoTrack() helper
  functions.
* Modify PySequence_Tuple() to use PyTupleWriter API.
* Soft deprecate _PyTuple_Resize().
@vstinner
vstinner force-pushed the tuplewriter_stack branch from 40be397 to e5a4e4e Compare October 14, 2025 18:40
@vstinner

vstinner commented Oct 14, 2025

Copy link
Copy Markdown
Member Author

Benchmark based on my previous benchmark.

Compare PyTuple_SET_ITEM() to PyTupleWriter_AddSteal() where heap is PR gh-139891 and stack is this PR (gh-140129).

Benchmark tuple heap stack
tuple-1 31.3 ns 42.3 ns: 1.35x slower 39.1 ns: 1.25x slower
tuple-5 51.0 ns 70.4 ns: 1.38x slower 65.2 ns: 1.28x slower
tuple-10 74.6 ns 106 ns: 1.41x slower 96.7 ns: 1.30x slower
tuple-100 567 ns 803 ns: 1.42x slower 754 ns: 1.33x slower
tuple-1000 5.52 us 7.73 us: 1.40x slower 7.33 us: 1.33x slower
Geometric mean (ref) 1.39x slower 1.30x slower
Details
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 4e73be20e1b..2a1d0b85a23 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -2562,6 +2562,74 @@ toggle_reftrace_printer(PyObject *ob, PyObject *arg)
     Py_RETURN_NONE;
 }
 
+static PyObject *
+bench_tuple(PyObject *ob, PyObject *args)
+{
+    Py_ssize_t size, loops;
+    if (!PyArg_ParseTuple(args, "nn", &size, &loops)) {
+        return NULL;
+    }
+
+    PyTime_t t1, t2;
+    PyTime_PerfCounterRaw(&t1);
+    for (Py_ssize_t i=0; i < loops; i++) {
+        PyObject *tuple = PyTuple_New(size);
+        if (tuple == NULL) {
+            return NULL;
+        }
+
+        for (int i=0; i < size; i++) {
+            PyObject *item = PyLong_FromLong(i);
+            if (item == NULL) {
+                Py_DECREF(tuple);
+                return NULL;
+            }
+            PyTuple_SET_ITEM(tuple, i, item);
+        }
+
+        Py_DECREF(tuple);
+    }
+    PyTime_PerfCounterRaw(&t2);
+    return PyFloat_FromDouble(PyTime_AsSecondsDouble(t2 - t1));
+}
+
+static PyObject *
+bench_writer(PyObject *ob, PyObject *args)
+{
+    Py_ssize_t size, loops;
+    if (!PyArg_ParseTuple(args, "nn", &size, &loops)) {
+        return NULL;
+    }
+
+    PyTime_t t1, t2;
+    PyTime_PerfCounterRaw(&t1);
+    for (Py_ssize_t i=0; i < loops; i++) {
+        PyTupleWriter writer;
+        if (PyTupleWriter_Init(&writer, size) < 0) {
+            return NULL;
+        }
+
+        for (int i=0; i < size; i++) {
+            PyObject *item = PyLong_FromLong(i);
+            if (item == NULL) {
+                return NULL;
+            }
+            if (PyTupleWriter_AddSteal(&writer, item) < 0) {
+                PyTupleWriter_Discard(&writer);
+                return NULL;
+            }
+        }
+
+        PyObject *tuple = PyTupleWriter_Finish(&writer);
+        if (tuple == NULL) {
+            return NULL;
+        }
+        Py_DECREF(tuple);
+    }
+    PyTime_PerfCounterRaw(&t2);
+    return PyFloat_FromDouble(PyTime_AsSecondsDouble(t2 - t1));
+}
+
 static PyMethodDef TestMethods[] = {
     {"set_errno",               set_errno,                       METH_VARARGS},
     {"test_config",             test_config,                     METH_NOARGS},
@@ -2656,6 +2724,8 @@ static PyMethodDef TestMethods[] = {
     {"test_atexit", test_atexit, METH_NOARGS},
     {"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL},
     {"toggle_reftrace_printer", toggle_reftrace_printer, METH_O},
+    {"bench_tuple", bench_tuple, METH_VARARGS},
+    {"bench_writer", bench_writer, METH_VARARGS},
     {NULL, NULL} /* sentinel */
 };
 
maurycy reacted with confused emoji
All reactions
  • 😕 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

maurycy
maurycy reviewed Oct 16, 2025
View reviewed changes
Comment thread Include/cpython/tupleobject.h Resolved

Uh oh!

There was an error while loading. Please reload this page.

@vstinner @maurycy
Update Include/cpython/tupleobject.h
b72f2e5
Co-authored-by: Maurycy Pawłowski-Wieroński <[email protected]>
maurycy
maurycy reviewed Oct 16, 2025
View reviewed changes
Comment thread Objects/tupleobject.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Dec 2, 2025

Copy link
Copy Markdown
Member Author

I wrote this API to get rid of _PyTuple_Resize() which treats an immutable tuple as mutable. I would like to avoid that: do not provide any function to mutate an immutable tuple.

I failed to convince other core developers that this PyTupleWriter API is useful. Moreover, it's 1.4x slower than the current API based on macros (which mutate an immutable tuple!).

I prefer to give up on PyTupleWriter. It seems like _PyTuple_Resize() has to stay for a few more years. In the meanwhile, one option is to create a list and then call PyList_AsTuple() to convert it to a tuple.

maurycy reacted with eyes emoji
All reactions
  • 👀 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner vstinner closed this Dec 2, 2025
@vstinner
vstinner deleted the tuplewriter_stack branch December 2, 2025 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@AA-Turner AA-Turner Awaiting requested review from AA-Turner AA-Turner will be requested when the pull request is marked ready for review AA-Turner is a code owner
@ZeroIntensity ZeroIntensity Awaiting requested review from ZeroIntensity ZeroIntensity will be requested when the pull request is marked ready for review ZeroIntensity is a code owner
1 more reviewer
@maurycy maurycy maurycy left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@vstinner @maurycy
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.