diff --git a/Include/internal/pycore_tracemalloc.h b/Include/internal/pycore_tracemalloc.h index 3fb0aa133fc41d..572e8025876319 100644 --- a/Include/internal/pycore_tracemalloc.h +++ b/Include/internal/pycore_tracemalloc.h @@ -70,7 +70,7 @@ struct _tracemalloc_runtime_state { PyMemAllocatorEx obj; } allocators; - PyThread_type_lock tables_lock; + PyMutex tables_lock; /* Size in bytes of currently traced memory. Protected by TABLES_LOCK(). */ size_t traced_memory; diff --git a/Misc/NEWS.d/next/C_API/2024-12-14-03-40-15.gh-issue-127925.FF7aov.rst b/Misc/NEWS.d/next/C_API/2024-12-14-03-40-15.gh-issue-127925.FF7aov.rst new file mode 100644 index 00000000000000..6cf5fd2872cd43 --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2024-12-14-03-40-15.gh-issue-127925.FF7aov.rst @@ -0,0 +1,3 @@ +Convert the :mod:`decimal` module to use :pep:`757` C API (export-import +integers), offering some speed-up if the integer part of the +:class:`~decimal.Decimal` instance is small. Patch by Sergey B Kirpichev. diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 0def463c7d8b9e..b9abd8bd2e7a53 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -2336,15 +2336,16 @@ dec_from_long(decimal_state *state, PyTypeObject *type, PyObject *v, } if (export_long.digits) { const PyLongLayout *layout = PyLong_GetNativeLayout(); - uint32_t base = (uint32_t)1 << layout->bits_per_digit; - uint8_t sign = export_long.negative ? MPD_NEG : MPD_POS; - Py_ssize_t len = export_long.ndigits; - assert(layout->bits_per_digit <= 32); + assert(layout->bits_per_digit < 32); assert(layout->digits_order == -1); assert(layout->digit_endianness == (PY_LITTLE_ENDIAN ? -1 : 1)); assert(layout->digit_size == 2 || layout->digit_size == 4); + uint32_t base = (uint32_t)1 << layout->bits_per_digit; + uint8_t sign = export_long.negative ? MPD_NEG : MPD_POS; + Py_ssize_t len = export_long.ndigits; + if (layout->digit_size == 4) { mpd_qimport_u32(MPD(dec), export_long.digits, len, sign, base, ctx, status); @@ -3642,13 +3643,6 @@ dec_format(PyObject *dec, PyObject *args) static PyObject * dec_as_long(PyObject *dec, PyObject *context, int round) { - PyLongObject *pylong; - digit *ob_digit; - size_t n; - mpd_t *x; - mpd_context_t workctx; - uint32_t status = 0; - if (mpd_isspecial(MPD(dec))) { if (mpd_isnan(MPD(dec))) { PyErr_SetString(PyExc_ValueError, @@ -3661,12 +3655,16 @@ dec_as_long(PyObject *dec, PyObject *context, int round) return NULL; } - x = mpd_qnew(); + mpd_t *x = mpd_qnew(); + if (x == NULL) { PyErr_NoMemory(); return NULL; } - workctx = *CTX(context); + + mpd_context_t workctx = *CTX(context); + uint32_t status = 0; + workctx.round = round; mpd_qround_to_int(x, MPD(dec), &workctx, &status); if (dec_addstatus(context, status)) { @@ -3675,34 +3673,56 @@ dec_as_long(PyObject *dec, PyObject *context, int round) } status = 0; - ob_digit = NULL; -#if PYLONG_BITS_IN_DIGIT == 30 - n = mpd_qexport_u32(&ob_digit, 0, PyLong_BASE, x, &status); -#elif PYLONG_BITS_IN_DIGIT == 15 - n = mpd_qexport_u16(&ob_digit, 0, PyLong_BASE, x, &status); -#else - #error "PYLONG_BITS_IN_DIGIT should be 15 or 30" -#endif + int64_t val = mpd_qget_i64(x, &status); + + if (!status) { + mpd_del(x); + return PyLong_FromInt64(val); + } + assert(!mpd_iszero(x)); + + const PyLongLayout *layout = PyLong_GetNativeLayout(); + + assert(layout->bits_per_digit < 32); + assert(layout->digits_order == -1); + assert(layout->digit_endianness == (PY_LITTLE_ENDIAN ? -1 : 1)); + assert(layout->digit_size == 2 || layout->digit_size == 4); + + uint32_t base = (uint32_t)1 << layout->bits_per_digit; + /* We use a temporary buffer for digits for now, as for nonzero rdata + mpd_qexport_u32/u16() require either space "allocated by one of + libmpdec’s allocation functions" or "rlen MUST be correct" (to avoid + reallocation). This can be further optimized by using rlen from + mpd_sizeinbase(). See gh-127925. */ + void *tmp_digits = NULL; + size_t n; + + status = 0; + if (layout->digit_size == 4) { + n = mpd_qexport_u32((uint32_t **)&tmp_digits, 0, base, x, &status); + } + else { + n = mpd_qexport_u16((uint16_t **)&tmp_digits, 0, base, x, &status); + } if (n == SIZE_MAX) { PyErr_NoMemory(); mpd_del(x); + mpd_free(tmp_digits); return NULL; } - if (n == 1) { - sdigit val = mpd_arith_sign(x) * ob_digit[0]; - mpd_free(ob_digit); - mpd_del(x); - return PyLong_FromLong(val); - } + void *digits; + PyLongWriter *writer = PyLongWriter_Create(mpd_isnegative(x), n, &digits); - assert(n > 0); - assert(!mpd_iszero(x)); - pylong = _PyLong_FromDigits(mpd_isnegative(x), n, ob_digit); - mpd_free(ob_digit); mpd_del(x); - return (PyObject *) pylong; + if (writer == NULL) { + mpd_free(tmp_digits); + return NULL; + } + memcpy(digits, tmp_digits, layout->digit_size*n); + mpd_free(tmp_digits); + return PyLongWriter_Finish(writer); } /* Convert a Decimal to its exact integer ratio representation. */ diff --git a/Modules/_json.c b/Modules/_json.c index a99abbe72bf7a0..091bcbfdced42b 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -353,6 +353,13 @@ _build_rval_index_tuple(PyObject *rval, Py_ssize_t idx) { return tpl; } +static inline int +_PyUnicodeWriter_IsEmpty(PyUnicodeWriter *writer_pub) +{ + _PyUnicodeWriter *writer = (_PyUnicodeWriter*)writer_pub; + return (writer->pos == 0); +} + static PyObject * scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next_end_ptr) { @@ -371,9 +378,10 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next const void *buf; int kind; - _PyUnicodeWriter writer; - _PyUnicodeWriter_Init(&writer); - writer.overallocate = 1; + PyUnicodeWriter *writer = PyUnicodeWriter_Create(0); + if (writer == NULL) { + goto bail; + } len = PyUnicode_GET_LENGTH(pystr); buf = PyUnicode_DATA(pystr); @@ -404,7 +412,7 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next if (c == '"') { // Fast path for simple case. - if (writer.buffer == NULL) { + if (_PyUnicodeWriter_IsEmpty(writer)) { PyObject *ret = PyUnicode_Substring(pystr, end, next); if (ret == NULL) { goto bail; @@ -420,7 +428,7 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next /* Pick up this chunk if it's not zero length */ if (next != end) { - if (_PyUnicodeWriter_WriteSubstring(&writer, pystr, end, next) < 0) { + if (PyUnicodeWriter_WriteSubstring(writer, pystr, end, next) < 0) { goto bail; } } @@ -511,18 +519,18 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next end -= 6; } } - if (_PyUnicodeWriter_WriteChar(&writer, c) < 0) { + if (PyUnicodeWriter_WriteChar(writer, c) < 0) { goto bail; } } - rval = _PyUnicodeWriter_Finish(&writer); + rval = PyUnicodeWriter_Finish(writer); *next_end_ptr = end; return rval; bail: *next_end_ptr = -1; - _PyUnicodeWriter_Dealloc(&writer); + PyUnicodeWriter_Discard(writer); return NULL; } diff --git a/Python/codecs.c b/Python/codecs.c index 07eaa8ecddcae0..53680a79082634 100644 --- a/Python/codecs.c +++ b/Python/codecs.c @@ -659,44 +659,64 @@ PyObject *PyCodec_LookupError(const char *name) return handler; } -static void wrong_exception_type(PyObject *exc) + +static inline void +wrong_exception_type(PyObject *exc) { PyErr_Format(PyExc_TypeError, - "don't know how to handle %.200s in error callback", - Py_TYPE(exc)->tp_name); + "don't know how to handle %T in error callback", exc); } + +#define _PyIsUnicodeEncodeError(EXC) \ + PyObject_TypeCheck(EXC, (PyTypeObject *)PyExc_UnicodeEncodeError) +#define _PyIsUnicodeDecodeError(EXC) \ + PyObject_TypeCheck(EXC, (PyTypeObject *)PyExc_UnicodeDecodeError) +#define _PyIsUnicodeTranslateError(EXC) \ + PyObject_TypeCheck(EXC, (PyTypeObject *)PyExc_UnicodeTranslateError) + + +// --- handler: 'strict' ------------------------------------------------------ + PyObject *PyCodec_StrictErrors(PyObject *exc) { - if (PyExceptionInstance_Check(exc)) + if (PyExceptionInstance_Check(exc)) { PyErr_SetObject(PyExceptionInstance_Class(exc), exc); - else + } + else { PyErr_SetString(PyExc_TypeError, "codec must pass exception instance"); + } return NULL; } -PyObject *PyCodec_IgnoreErrors(PyObject *exc) +// --- handler: 'ignore' ------------------------------------------------------ + +static PyObject * +_PyCodec_IgnoreError(PyObject *exc, int as_bytes) { Py_ssize_t end; - - if (PyObject_TypeCheck(exc, (PyTypeObject *)PyExc_UnicodeEncodeError)) { - if (PyUnicodeEncodeError_GetEnd(exc, &end)) - return NULL; + if (_PyUnicodeError_GetParams(exc, NULL, NULL, NULL, + &end, NULL, as_bytes) < 0) + { + return NULL; } - else if (PyObject_TypeCheck(exc, (PyTypeObject *)PyExc_UnicodeDecodeError)) { - if (PyUnicodeDecodeError_GetEnd(exc, &end)) - return NULL; + return Py_BuildValue("(Nn)", Py_GetConstant(Py_CONSTANT_EMPTY_STR), end); +} + + +PyObject *PyCodec_IgnoreErrors(PyObject *exc) +{ + if (_PyIsUnicodeEncodeError(exc) || _PyIsUnicodeTranslateError(exc)) { + return _PyCodec_IgnoreError(exc, false); } - else if (PyObject_TypeCheck(exc, (PyTypeObject *)PyExc_UnicodeTranslateError)) { - if (PyUnicodeTranslateError_GetEnd(exc, &end)) - return NULL; + else if (_PyIsUnicodeDecodeError(exc)) { + return _PyCodec_IgnoreError(exc, true); } else { wrong_exception_type(exc); return NULL; } - return Py_BuildValue("(Nn)", Py_GetConstant(Py_CONSTANT_EMPTY_STR), end); } @@ -1368,13 +1388,17 @@ PyCodec_SurrogateEscapeErrors(PyObject *exc) } -static PyObject *strict_errors(PyObject *self, PyObject *exc) +// --- Codecs registry handlers ----------------------------------------------- + +static inline PyObject * +strict_errors(PyObject *Py_UNUSED(self), PyObject *exc) { return PyCodec_StrictErrors(exc); } -static PyObject *ignore_errors(PyObject *self, PyObject *exc) +static inline PyObject * +ignore_errors(PyObject *Py_UNUSED(self), PyObject *exc) { return PyCodec_IgnoreErrors(exc); } diff --git a/Python/tracemalloc.c b/Python/tracemalloc.c index 20651016a80ec2..d27c2f9319ae58 100644 --- a/Python/tracemalloc.c +++ b/Python/tracemalloc.c @@ -3,6 +3,7 @@ #include "pycore_gc.h" // PyGC_Head #include "pycore_hashtable.h" // _Py_hashtable_t #include "pycore_initconfig.h" // _PyStatus_NO_MEMORY() +#include "pycore_lock.h" // PyMutex_LockFlags() #include "pycore_object.h" // _PyType_PreHeaderSize() #include "pycore_pymem.h" // _Py_tracemalloc_config #include "pycore_runtime.h" // _Py_ID() @@ -37,8 +38,8 @@ static int _PyTraceMalloc_TraceRef(PyObject *op, PyRefTracerEvent event, the GIL held from PyMem_RawFree(). It cannot acquire the lock because it would introduce a deadlock in _PyThreadState_DeleteCurrent(). */ #define tables_lock _PyRuntime.tracemalloc.tables_lock -#define TABLES_LOCK() PyThread_acquire_lock(tables_lock, 1) -#define TABLES_UNLOCK() PyThread_release_lock(tables_lock) +#define TABLES_LOCK() PyMutex_LockFlags(&tables_lock, _Py_LOCK_DONT_DETACH) +#define TABLES_UNLOCK() PyMutex_Unlock(&tables_lock) #define DEFAULT_DOMAIN 0 @@ -741,13 +742,6 @@ _PyTraceMalloc_Init(void) return _PyStatus_NO_MEMORY(); } - if (tables_lock == NULL) { - tables_lock = PyThread_allocate_lock(); - if (tables_lock == NULL) { - return _PyStatus_NO_MEMORY(); - } - } - tracemalloc_filenames = hashtable_new(hashtable_hash_pyobject, hashtable_compare_unicode, tracemalloc_clear_filename, NULL); @@ -792,11 +786,6 @@ tracemalloc_deinit(void) _Py_hashtable_destroy(tracemalloc_tracebacks); _Py_hashtable_destroy(tracemalloc_filenames); - if (tables_lock != NULL) { - PyThread_free_lock(tables_lock); - tables_lock = NULL; - } - PyThread_tss_delete(&tracemalloc_reentrant_key); }