From 757f48b0b150312f39c8d4855104c741ca9acdee Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 14 Apr 2025 22:13:22 +0200 Subject: [PATCH 1/6] ENH: Improve np.result_type performance --- numpy/_core/src/multiarray/multiarraymodule.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/numpy/_core/src/multiarray/multiarraymodule.c b/numpy/_core/src/multiarray/multiarraymodule.c index e74e14bf10c2..e0ad96ac8357 100644 --- a/numpy/_core/src/multiarray/multiarraymodule.c +++ b/numpy/_core/src/multiarray/multiarraymodule.c @@ -3583,7 +3583,6 @@ static PyObject * array_result_type(PyObject *NPY_UNUSED(dummy), PyObject *const *args, Py_ssize_t len) { npy_intp i, narr = 0, ndtypes = 0; - PyArrayObject **arr = NULL; PyArray_Descr **dtypes = NULL; PyObject *ret = NULL; @@ -3593,7 +3592,10 @@ array_result_type(PyObject *NPY_UNUSED(dummy), PyObject *const *args, Py_ssize_t goto finish; } - arr = PyArray_malloc(2 * len * sizeof(void *)); + NPY_ALLOC_WORKSPACE(arr, PyArrayObject *, 2 * 3, 2 * len); + if (arr == NULL) { + return NULL; + } if (arr == NULL) { return PyErr_NoMemory(); } @@ -3636,7 +3638,7 @@ array_result_type(PyObject *NPY_UNUSED(dummy), PyObject *const *args, Py_ssize_t for (i = 0; i < ndtypes; ++i) { Py_DECREF(dtypes[i]); } - PyArray_free(arr); + npy_free_workspace(arr); return ret; } From ec968e305b5a679f2a96266b094ad3b0c03f5cf6 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 14 Apr 2025 22:34:27 +0200 Subject: [PATCH 2/6] use borrowed references --- numpy/_core/src/multiarray/convert_datatype.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/numpy/_core/src/multiarray/convert_datatype.c b/numpy/_core/src/multiarray/convert_datatype.c index 7de76ac63ed6..59b6298b5815 100644 --- a/numpy/_core/src/multiarray/convert_datatype.c +++ b/numpy/_core/src/multiarray/convert_datatype.c @@ -1597,13 +1597,12 @@ PyArray_ResultType( return NULL; } - PyArray_DTypeMeta **all_DTypes = (PyArray_DTypeMeta **)workspace; + PyArray_DTypeMeta **all_DTypes = (PyArray_DTypeMeta **)workspace; // borrowed references PyArray_Descr **all_descriptors = (PyArray_Descr **)(&all_DTypes[narrs+ndtypes]); /* Copy all dtypes into a single array defining non-value-based behaviour */ for (npy_intp i=0; i < ndtypes; i++) { all_DTypes[i] = NPY_DTYPE(descrs[i]); - Py_INCREF(all_DTypes[i]); all_descriptors[i] = descrs[i]; } @@ -1628,14 +1627,10 @@ PyArray_ResultType( all_descriptors[i_all] = PyArray_DTYPE(arrs[i]); all_DTypes[i_all] = NPY_DTYPE(all_descriptors[i_all]); } - Py_INCREF(all_DTypes[i_all]); } PyArray_DTypeMeta *common_dtype = PyArray_PromoteDTypeSequence( narrs+ndtypes, all_DTypes); - for (npy_intp i=0; i < narrs+ndtypes; i++) { - Py_DECREF(all_DTypes[i]); - } if (common_dtype == NULL) { goto error; } From 4419d7c79d4ea8001e389d3a574c2fcc7294f377 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 15 Apr 2025 10:31:41 +0200 Subject: [PATCH 3/6] fast path for equal dtype arguments --- numpy/_core/src/multiarray/multiarraymodule.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/numpy/_core/src/multiarray/multiarraymodule.c b/numpy/_core/src/multiarray/multiarraymodule.c index e0ad96ac8357..422f9bb66e7c 100644 --- a/numpy/_core/src/multiarray/multiarraymodule.c +++ b/numpy/_core/src/multiarray/multiarraymodule.c @@ -3601,8 +3601,13 @@ array_result_type(PyObject *NPY_UNUSED(dummy), PyObject *const *args, Py_ssize_t } dtypes = (PyArray_Descr**)&arr[len]; + PyObject *previous_obj = NULL; + for (i = 0; i < len; ++i) { PyObject *obj = args[i]; + if (obj == previous_obj) + continue; + if (PyArray_Check(obj)) { Py_INCREF(obj); arr[narr] = (PyArrayObject *)obj; From 4b4125822155ff7e262d5bb90d0b127afaa04e5f Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 15 Apr 2025 10:32:11 +0200 Subject: [PATCH 4/6] fast path in array method lookup for dtypes --- numpy/_core/src/common/get_attr_string.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/numpy/_core/src/common/get_attr_string.h b/numpy/_core/src/common/get_attr_string.h index 324a92c5ef0c..11e777b420c9 100644 --- a/numpy/_core/src/common/get_attr_string.h +++ b/numpy/_core/src/common/get_attr_string.h @@ -3,6 +3,7 @@ #include #include "npy_pycompat.h" +#include "numpy/ndarraytypes.h" static inline npy_bool @@ -59,6 +60,11 @@ PyArray_LookupSpecial( return 0; } + /* We do not need to check for special attributes on array descriptor types */ + if (PyObject_TypeCheck(tp, &PyArrayDTypeMeta_Type)) { + *res = NULL; + return 0; + } return PyObject_GetOptionalAttr((PyObject *)tp, name_unicode, res); } From 112b54edd603e8393f98392fb6408a9ce21a8f3d Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 15 Apr 2025 14:37:15 +0200 Subject: [PATCH 5/6] fix compiler warning --- numpy/_core/src/multiarray/multiarraymodule.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/numpy/_core/src/multiarray/multiarraymodule.c b/numpy/_core/src/multiarray/multiarraymodule.c index 422f9bb66e7c..4c5b39c9e42c 100644 --- a/numpy/_core/src/multiarray/multiarraymodule.c +++ b/numpy/_core/src/multiarray/multiarraymodule.c @@ -3583,13 +3583,12 @@ static PyObject * array_result_type(PyObject *NPY_UNUSED(dummy), PyObject *const *args, Py_ssize_t len) { npy_intp i, narr = 0, ndtypes = 0; - PyArray_Descr **dtypes = NULL; PyObject *ret = NULL; if (len == 0) { PyErr_SetString(PyExc_ValueError, "at least one array or dtype is required"); - goto finish; + return NULL; } NPY_ALLOC_WORKSPACE(arr, PyArrayObject *, 2 * 3, 2 * len); @@ -3599,7 +3598,7 @@ array_result_type(PyObject *NPY_UNUSED(dummy), PyObject *const *args, Py_ssize_t if (arr == NULL) { return PyErr_NoMemory(); } - dtypes = (PyArray_Descr**)&arr[len]; + PyArray_Descr **dtypes = (PyArray_Descr**)&arr[len]; PyObject *previous_obj = NULL; From 9e836b8cb570808fad6ae94e834878f58968dd73 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 15 Apr 2025 16:38:46 +0200 Subject: [PATCH 6/6] review comments --- numpy/_core/src/common/get_attr_string.h | 6 ------ numpy/_core/src/multiarray/multiarraymodule.c | 6 ++---- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/numpy/_core/src/common/get_attr_string.h b/numpy/_core/src/common/get_attr_string.h index 11e777b420c9..324a92c5ef0c 100644 --- a/numpy/_core/src/common/get_attr_string.h +++ b/numpy/_core/src/common/get_attr_string.h @@ -3,7 +3,6 @@ #include #include "npy_pycompat.h" -#include "numpy/ndarraytypes.h" static inline npy_bool @@ -60,11 +59,6 @@ PyArray_LookupSpecial( return 0; } - /* We do not need to check for special attributes on array descriptor types */ - if (PyObject_TypeCheck(tp, &PyArrayDTypeMeta_Type)) { - *res = NULL; - return 0; - } return PyObject_GetOptionalAttr((PyObject *)tp, name_unicode, res); } diff --git a/numpy/_core/src/multiarray/multiarraymodule.c b/numpy/_core/src/multiarray/multiarraymodule.c index 4c5b39c9e42c..cddfad16a972 100644 --- a/numpy/_core/src/multiarray/multiarraymodule.c +++ b/numpy/_core/src/multiarray/multiarraymodule.c @@ -3595,17 +3595,15 @@ array_result_type(PyObject *NPY_UNUSED(dummy), PyObject *const *args, Py_ssize_t if (arr == NULL) { return NULL; } - if (arr == NULL) { - return PyErr_NoMemory(); - } PyArray_Descr **dtypes = (PyArray_Descr**)&arr[len]; PyObject *previous_obj = NULL; for (i = 0; i < len; ++i) { PyObject *obj = args[i]; - if (obj == previous_obj) + if (obj == previous_obj) { continue; + } if (PyArray_Check(obj)) { Py_INCREF(obj);