Skip to content

gh-87613: Allow Argument Clinic vectorcall on heap types - #160115

Merged
vstinner merged 5 commits into
python:mainfrom
cmaloney:gh-150097-clinic-vectorcall-heaptype
Oct 23, 2026
Merged

vstinner merged 5 commits into
python:mainfrom
cmaloney:gh-150097-clinic-vectorcall-heaptype

Conversation

@cmaloney

@cmaloney cmaloney commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

decimal.Decimal is a heap type that would like to use AC Vectorcall. Heap types have no static type object so switch from Py_Is to an assert that the shadowed tp_new / tp_init function is the expected one to validate the vectorcall is ony used with the exact function it is providing a "vector" option for.

Heap types can have zero-argument __new__ and __init__ so add support for that producing the same error message via a new helper _PyArg_NoPositionalStack().

--
Minified from GH-157968. This does not use Py_tp_token as that only exists on Heap objects and want both static and heap objects to work with the same code.

Heap types have no static type object so switch from `Py_Is` to an
assert that the shadowed `tp_new` / `tp_init` function is the expected
one to validate the vectorcall is onl¥ used with the right type.

Heap types can have zero-argument `__new__` and `__init__` so add
support  for that producing the same error message via a new helper
`_PyArg_NoPositionalStack()`.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@bedevere-app bedevere-app Bot added the type-feature A feature request or enhancement label Oct 21, 2026
@cmaloney cmaloney changed the title gh-87613: Allow Argument Clinic @vectorcall on heap types gh-87613: Allow Argument Clinic vectorcall on heap types Oct 21, 2026
…nic-vectorcall-heaptype

# Conflicts:
#	Objects/clinic/bytearrayobject.c.h
@skirpichev

Copy link
Copy Markdown
Member

Has merge conflicts.

@skirpichev

Copy link
Copy Markdown
Member

Maybe you include Decimal case in this pr?

cmaloney and others added 2 commits October 22, 2026 09:51
…nic-vectorcall-heaptype

# Conflicts:
#	Objects/clinic/bytearrayobject.c.h
@cmaloney

Copy link
Copy Markdown
Contributor Author

@skirpichev uipdated to fix conflicts and include Decimal

@skirpichev

Copy link
Copy Markdown
Member

With same benchmark setup as in #158207:

decimal_factorial: Mean +- std dev: [ref] 1.06 sec +- 0.02 sec -> [patch] 1.06 sec +- 0.01 sec: 1.00x faster
decimal_pi: Mean +- std dev: [ref] 1.90 sec +- 0.02 sec -> [patch] 1.87 sec +- 0.01 sec: 1.02x faster

Geometric mean: 1.01x faster
Decimal(0): Mean +- std dev: [ref-micro] 584 ns +- 7 ns -> [patch-micro] 513 ns +- 8 ns: 1.14x faster
Decimal(123): Mean +- std dev: [ref-micro] 584 ns +- 10 ns -> [patch-micro] 515 ns +- 6 ns: 1.13x faster
Decimal('123'): Mean +- std dev: [ref-micro] 743 ns +- 14 ns -> [patch-micro] 704 ns +- 11 ns: 1.06x faster

Geometric mean: 1.11x faster

CC @vstinner

@cmaloney

Copy link
Copy Markdown
Contributor Author

static PyObject *
PyDecType_FromObjectExact(PyTypeObject *type, PyObject *v, PyObject *context)
{
decimal_state *state = get_module_state_from_ctx(context);
if (v == NULL) {
return PyDecType_FromSsizeExact(type, 0, context);
}
else if (PyDec_Check(state, v)) {
return PyDecType_FromDecimalExact(type, v, context);
}
else if (PyUnicode_Check(v)) {
return PyDecType_FromUnicodeExactWS(type, v, context);
}
else if (PyLong_Check(v)) {
return PyDecType_FromLongExact(type, v, context);
}
else if (PyTuple_Check(v) || PyList_Check(v)) {
return PyDecType_FromSequenceExact(type, v, context);
}
else if (PyFloat_Check(v)) {
if (dec_addstatus(context, MPD_Float_operation)) {
return NULL;
}
return PyDecType_FromFloatExact(type, v, context);
}
else {
PyErr_Format(PyExc_TypeError,
"conversion from %s to Decimal is not supported",
Py_TYPE(v)->tp_name);
return NULL;
}
}

There is quite a bit more performance that should be unlockable for decimal construction if we get so Argument Clinic can inline the type check tree and/or provide to the interpreter dispatch loop a set of typed overloads (reduces instruction count a lot...). For bytes inlining that makes VC go from 1.1x -> 1.6x over fastcall.

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM if you revert the _decimal change.

Comment thread Modules/_decimal/_decimal.c Outdated

/*[clinic input]
@classmethod
@vectorcall

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert the _decimal change. I would prefer a dedicated change for that, with benchmarks.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, there are benchmarks. IMO, it would be better to leave this suggestion for a separate pr: #160115 (comment)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I would still prefer to change AC first, and then change decimal in a separated PR.

Thanks for the benchmarks, I missed them.

Per review, change decimal in a separate PR.

This reverts commit 1ce0827.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@vstinner
vstinner enabled auto-merge (squash) October 23, 2026 00:25
@vstinner
vstinner merged commit 3fc4d1f into python:main Oct 23, 2026
68 of 69 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type-feature A feature request or enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants