diff --git a/Misc/NEWS.d/next/Library/2026-09-26-05-18-21.gh-issue-144650.zqHLOW.rst b/Misc/NEWS.d/next/Library/2026-09-26-05-18-21.gh-issue-144650.zqHLOW.rst new file mode 100644 index 000000000000000..34a0c0246d3ee84 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-26-05-18-21.gh-issue-144650.zqHLOW.rst @@ -0,0 +1,2 @@ +Use AC vectorcall support for the :class:`decimal.Decimal`. This offers +~1.15x speedup on creation of small instances. diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 5eaba2fdbdf2d6a..c4f4610084b791e 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -3229,7 +3229,7 @@ dec_apply(PyObject *v, PyObject *context) /* 'v' can have any type accepted by the Decimal constructor. Attempt an exact conversion. If the result does not meet the restrictions for an mpd_t, fail with InvalidOperation. */ -static PyObject * +static inline PyObject * PyDecType_FromObjectExact(PyTypeObject *type, PyObject *v, PyObject *context) { decimal_state *state = get_module_state_from_ctx(context); @@ -3313,6 +3313,7 @@ PyDec_FromObject(PyObject *v, PyObject *context) } /*[clinic input] +@vectorcall @classmethod _decimal.Decimal.__new__ as dec_new @@ -3329,7 +3330,7 @@ trap is active. static PyObject * dec_new_impl(PyTypeObject *type, PyObject *value, PyObject *context) -/*[clinic end generated code: output=35f48a40c65625ba input=5f8a0892d3fcef80]*/ +/*[clinic end generated code: output=35f48a40c65625ba input=0e5ca99183562cd9]*/ { decimal_state *state = get_module_state_by_def(type); CONTEXT_CHECK_VA(state, context); @@ -6190,6 +6191,7 @@ static PyType_Slot dec_slots[] = { {Py_tp_methods, dec_methods}, {Py_tp_getset, dec_getsets}, {Py_tp_new, dec_new}, + {Py_tp_vectorcall, dec_vectorcall}, // Number protocol {Py_nb_add, nm_mpd_qadd}, diff --git a/Modules/_decimal/clinic/_decimal.c.h b/Modules/_decimal/clinic/_decimal.c.h index bbf5613848f13e0..ed16aa420109e86 100644 --- a/Modules/_decimal/clinic/_decimal.c.h +++ b/Modules/_decimal/clinic/_decimal.c.h @@ -867,7 +867,8 @@ static PyObject * dec_new_impl(PyTypeObject *type, PyObject *value, PyObject *context); static PyObject * -dec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +dec_new_helper(PyTypeObject *type, PyObject *const *args, + Py_ssize_t nargs, Py_ssize_t nkw, PyObject *kwargs, PyObject *kwnames) { PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) @@ -899,12 +900,11 @@ dec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) #undef KWTUPLE PyObject *argsbuf[2]; PyObject * const *fastargs; - Py_ssize_t nargs = PyTuple_GET_SIZE(args); - Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; + Py_ssize_t noptargs = nargs + nkw - 0; PyObject *value = NULL; PyObject *context = Py_None; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, + fastargs = _PyArg_UnpackKeywords(args, nargs, kwargs, kwnames, &_parser, /*minpos*/ 0, /*maxpos*/ 2, /*minkw*/ 0, /*varpos*/ 0, argsbuf); if (!fastargs) { goto exit; @@ -926,6 +926,47 @@ dec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) return return_value; } +static PyObject * +dec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + return dec_new_helper(type, _PyTuple_CAST(args)->ob_item, + PyTuple_GET_SIZE(args), + kwargs ? PyDict_GET_SIZE(kwargs) : 0, + kwargs, NULL); +} + +static PyObject * +dec_vectorcall(PyObject *type, PyObject *const *args, + size_t nargsf, PyObject *kwnames) +{ + PyObject *return_value = NULL; + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + PyObject *value = NULL; + PyObject *context = Py_None; + + assert(_PyType_CAST(type)->tp_new == dec_new); + /* Make sure the type object is immutable: the generated + * vectorcall doesn't deal e.g. with users reassigning __init__. */ + assert(PyType_HasFeature(_PyType_CAST(type), Py_TPFLAGS_IMMUTABLETYPE)); + if (kwnames != NULL || nargs > 2) { + return dec_new_helper(_PyType_CAST(type), args, nargs, + kwnames ? PyTuple_GET_SIZE(kwnames) : 0, + NULL, kwnames); + } + if (nargs < 1) { + goto skip_optional; + } + value = args[0]; + if (nargs < 2) { + goto skip_optional; + } + context = args[1]; +skip_optional: + return_value = dec_new_impl(_PyType_CAST(type), value, context); + + return return_value; +} + PyDoc_STRVAR(_decimal_Context_create_decimal__doc__, "create_decimal($self, num=\'0\', /)\n" "--\n" @@ -6283,4 +6324,4 @@ _decimal_Context_same_quantum(PyObject *context, PyTypeObject *cls, PyObject *co #ifndef _DECIMAL_CONTEXT_APPLY_METHODDEF #define _DECIMAL_CONTEXT_APPLY_METHODDEF #endif /* !defined(_DECIMAL_CONTEXT_APPLY_METHODDEF) */ -/*[clinic end generated code: output=783d0db339199a30 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=10892d647ee2a899 input=a9049054013a1b77]*/