Skip to content

Commit 6599834

Browse files
authored
PERF: tighter cython declarations, faster __iter__ (#43872)
1 parent 4e8b77d commit 6599834

File tree

5 files changed

+51
-36
lines changed

5 files changed

+51
-36
lines changed

pandas/_libs/algos_common_helper.pxi.in

+2-4
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,16 @@ WARNING: DO NOT edit .pxi FILE directly, .pxi is generated from .pxi.in
88
# ensure_dtype
99
# ----------------------------------------------------------------------
1010

11-
cdef int PLATFORM_INT = (<ndarray>np.arange(0, dtype=np.intp)).descr.type_num
12-
1311

1412
def ensure_platform_int(object arr):
1513
# GH3033, GH1392
1614
# platform int is the size of the int pointer, e.g. np.intp
1715
if util.is_array(arr):
18-
if (<ndarray>arr).descr.type_num == PLATFORM_INT:
16+
if (<ndarray>arr).descr.type_num == cnp.NPY_INTP:
1917
return arr
2018
else:
2119
# equiv: arr.astype(np.intp)
22-
return cnp.PyArray_Cast(<ndarray>arr, PLATFORM_INT)
20+
return cnp.PyArray_Cast(<ndarray>arr, cnp.NPY_INTP)
2321
else:
2422
return np.array(arr, dtype=np.intp)
2523

pandas/_libs/algos_take_helper.pxi.in

+4-4
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def take_2d_axis0_{{name}}_{{dest}}(const {{c_type_in}}[:, :] values,
103103
{{else}}
104104
def take_2d_axis0_{{name}}_{{dest}}(ndarray[{{c_type_in}}, ndim=2] values,
105105
{{endif}}
106-
ndarray[intp_t] indexer,
106+
ndarray[intp_t, ndim=1] indexer,
107107
{{c_type_out}}[:, :] out,
108108
fill_value=np.nan):
109109
cdef:
@@ -158,7 +158,7 @@ def take_2d_axis1_{{name}}_{{dest}}(const {{c_type_in}}[:, :] values,
158158
{{else}}
159159
def take_2d_axis1_{{name}}_{{dest}}(ndarray[{{c_type_in}}, ndim=2] values,
160160
{{endif}}
161-
ndarray[intp_t] indexer,
161+
ndarray[intp_t, ndim=1] indexer,
162162
{{c_type_out}}[:, :] out,
163163
fill_value=np.nan):
164164

@@ -195,8 +195,8 @@ def take_2d_multi_{{name}}_{{dest}}(ndarray[{{c_type_in}}, ndim=2] values,
195195
fill_value=np.nan):
196196
cdef:
197197
Py_ssize_t i, j, k, n, idx
198-
ndarray[intp_t] idx0 = indexer[0]
199-
ndarray[intp_t] idx1 = indexer[1]
198+
ndarray[intp_t, ndim=1] idx0 = indexer[0]
199+
ndarray[intp_t, ndim=1] idx1 = indexer[1]
200200
{{c_type_out}} fv
201201

202202
n = len(idx0)

pandas/_libs/internals.pyx

+23-9
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ cdef class BlockPlacement:
227227
cdef:
228228
slice nv, s = self._ensure_has_slice()
229229
Py_ssize_t other_int, start, stop, step, l
230-
ndarray newarr
230+
ndarray[intp_t, ndim=1] newarr
231231

232232
if s is not None:
233233
# see if we are either all-above or all-below, each of which
@@ -260,7 +260,7 @@ cdef class BlockPlacement:
260260
cdef:
261261
slice slc = self._ensure_has_slice()
262262
slice new_slice
263-
ndarray new_placement
263+
ndarray[intp_t, ndim=1] new_placement
264264

265265
if slc is not None and slc.step == 1:
266266
new_slc = slice(slc.start * factor, slc.stop * factor, 1)
@@ -345,7 +345,9 @@ cpdef Py_ssize_t slice_len(slice slc, Py_ssize_t objlen=PY_SSIZE_T_MAX) except -
345345
return length
346346

347347

348-
cdef slice_get_indices_ex(slice slc, Py_ssize_t objlen=PY_SSIZE_T_MAX):
348+
cdef (Py_ssize_t, Py_ssize_t, Py_ssize_t, Py_ssize_t) slice_get_indices_ex(
349+
slice slc, Py_ssize_t objlen=PY_SSIZE_T_MAX
350+
):
349351
"""
350352
Get (start, stop, step, length) tuple for a slice.
351353
@@ -460,9 +462,11 @@ def get_blkno_indexers(
460462
# blockno handling.
461463
cdef:
462464
int64_t cur_blkno
463-
Py_ssize_t i, start, stop, n, diff, tot_len
465+
Py_ssize_t i, start, stop, n, diff
466+
cnp.npy_intp tot_len
464467
int64_t blkno
465468
object group_dict = defaultdict(list)
469+
ndarray[int64_t, ndim=1] arr
466470

467471
n = blknos.shape[0]
468472
result = list()
@@ -495,7 +499,8 @@ def get_blkno_indexers(
495499
result.append((blkno, slice(slices[0][0], slices[0][1])))
496500
else:
497501
tot_len = sum(stop - start for start, stop in slices)
498-
arr = np.empty(tot_len, dtype=np.int64)
502+
# equiv np.empty(tot_len, dtype=np.int64)
503+
arr = cnp.PyArray_EMPTY(1, &tot_len, cnp.NPY_INT64, 0)
499504

500505
i = 0
501506
for start, stop in slices:
@@ -526,16 +531,21 @@ def get_blkno_placements(blknos, group: bool = True):
526531
yield blkno, BlockPlacement(indexer)
527532

528533

534+
@cython.boundscheck(False)
535+
@cython.wraparound(False)
529536
cpdef update_blklocs_and_blknos(
530-
ndarray[intp_t] blklocs, ndarray[intp_t] blknos, Py_ssize_t loc, intp_t nblocks
537+
ndarray[intp_t, ndim=1] blklocs,
538+
ndarray[intp_t, ndim=1] blknos,
539+
Py_ssize_t loc,
540+
intp_t nblocks,
531541
):
532542
"""
533543
Update blklocs and blknos when a new column is inserted at 'loc'.
534544
"""
535545
cdef:
536546
Py_ssize_t i
537547
cnp.npy_intp length = len(blklocs) + 1
538-
ndarray[intp_t] new_blklocs, new_blknos
548+
ndarray[intp_t, ndim=1] new_blklocs, new_blknos
539549

540550
# equiv: new_blklocs = np.empty(length, dtype=np.intp)
541551
new_blklocs = cnp.PyArray_EMPTY(1, &length, cnp.NPY_INTP, 0)
@@ -693,7 +703,7 @@ cdef class BlockManager:
693703
cnp.npy_intp length = self.shape[0]
694704
SharedBlock blk
695705
BlockPlacement bp
696-
ndarray[intp_t] new_blknos, new_blklocs
706+
ndarray[intp_t, ndim=1] new_blknos, new_blklocs
697707

698708
# equiv: np.empty(length, dtype=np.intp)
699709
new_blknos = cnp.PyArray_EMPTY(1, &length, cnp.NPY_INTP, 0)
@@ -711,7 +721,11 @@ cdef class BlockManager:
711721
new_blknos[j] = blkno
712722
new_blklocs[j] = i
713723

714-
for blkno in new_blknos:
724+
for i in range(length):
725+
# faster than `for blkno in new_blknos`
726+
# https://github.com/cython/cython/issues/4393
727+
blkno = new_blknos[i]
728+
715729
# If there are any -1s remaining, this indicates that our mgr_locs
716730
# are invalid.
717731
if blkno == -1:

pandas/_libs/lib.pyx

+20-16
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ def fast_zip(list ndarrays) -> ndarray[object]:
448448
"""
449449
cdef:
450450
Py_ssize_t i, j, k, n
451-
ndarray[object] result
451+
ndarray[object, ndim=1] result
452452
flatiter it
453453
object val, tup
454454

@@ -507,7 +507,7 @@ def get_reverse_indexer(const intp_t[:] indexer, Py_ssize_t length) -> ndarray:
507507
"""
508508
cdef:
509509
Py_ssize_t i, n = len(indexer)
510-
ndarray[intp_t] rev_indexer
510+
ndarray[intp_t, ndim=1] rev_indexer
511511
intp_t idx
512512

513513
rev_indexer = np.empty(length, dtype=np.intp)
@@ -540,7 +540,7 @@ def has_infs(floating[:] arr) -> bool:
540540
return ret
541541

542542

543-
def maybe_indices_to_slice(ndarray[intp_t] indices, int max_len):
543+
def maybe_indices_to_slice(ndarray[intp_t, ndim=1] indices, int max_len):
544544
cdef:
545545
Py_ssize_t i, n = len(indices)
546546
int k, vstart, vlast, v
@@ -579,7 +579,7 @@ def maybe_indices_to_slice(ndarray[intp_t] indices, int max_len):
579579

580580
@cython.wraparound(False)
581581
@cython.boundscheck(False)
582-
def maybe_booleans_to_slice(ndarray[uint8_t] mask):
582+
def maybe_booleans_to_slice(ndarray[uint8_t, ndim=1] mask):
583583
cdef:
584584
Py_ssize_t i, n = len(mask)
585585
Py_ssize_t start = 0, end = 0
@@ -775,14 +775,14 @@ def is_all_arraylike(obj: list) -> bool:
775775
# is a general, O(max(len(values), len(binner))) method.
776776
@cython.boundscheck(False)
777777
@cython.wraparound(False)
778-
def generate_bins_dt64(ndarray[int64_t] values, const int64_t[:] binner,
778+
def generate_bins_dt64(ndarray[int64_t, ndim=1] values, const int64_t[:] binner,
779779
object closed='left', bint hasnans=False):
780780
"""
781781
Int64 (datetime64) version of generic python version in ``groupby.py``.
782782
"""
783783
cdef:
784784
Py_ssize_t lenidx, lenbin, i, j, bc, vc
785-
ndarray[int64_t] bins
785+
ndarray[int64_t, ndim=1] bins
786786
int64_t l_bin, r_bin, nat_count
787787
bint right_closed = closed == 'right'
788788

@@ -931,7 +931,7 @@ def generate_slices(const intp_t[:] labels, Py_ssize_t ngroups):
931931
return np.asarray(starts), np.asarray(ends)
932932

933933

934-
def indices_fast(ndarray[intp_t] index, const int64_t[:] labels, list keys,
934+
def indices_fast(ndarray[intp_t, ndim=1] index, const int64_t[:] labels, list keys,
935935
list sorted_labels) -> dict:
936936
"""
937937
Parameters
@@ -2067,7 +2067,9 @@ cdef bint is_period_array(ndarray[object] values):
20672067
if len(values) == 0:
20682068
return False
20692069

2070-
for val in values:
2070+
for i in range(n):
2071+
val = values[i]
2072+
20712073
if is_period_object(val):
20722074
if dtype_code == -10000:
20732075
dtype_code = val._dtype._dtype_code
@@ -2102,7 +2104,9 @@ cpdef bint is_interval_array(ndarray values):
21022104
if len(values) == 0:
21032105
return False
21042106

2105-
for val in values:
2107+
for i in range(n):
2108+
val = values[i]
2109+
21062110
if is_interval(val):
21072111
if closed is None:
21082112
closed = val.closed
@@ -2144,7 +2148,7 @@ cpdef bint is_interval_array(ndarray values):
21442148
@cython.boundscheck(False)
21452149
@cython.wraparound(False)
21462150
def maybe_convert_numeric(
2147-
ndarray[object] values,
2151+
ndarray[object, ndim=1] values,
21482152
set na_values,
21492153
bint convert_empty=True,
21502154
bint coerce_numeric=False,
@@ -2205,12 +2209,12 @@ def maybe_convert_numeric(
22052209
int status, maybe_int
22062210
Py_ssize_t i, n = values.size
22072211
Seen seen = Seen(coerce_numeric)
2208-
ndarray[float64_t] floats = np.empty(n, dtype='f8')
2209-
ndarray[complex128_t] complexes = np.empty(n, dtype='c16')
2210-
ndarray[int64_t] ints = np.empty(n, dtype='i8')
2211-
ndarray[uint64_t] uints = np.empty(n, dtype='u8')
2212-
ndarray[uint8_t] bools = np.empty(n, dtype='u1')
2213-
ndarray[uint8_t] mask = np.zeros(n, dtype="u1")
2212+
ndarray[float64_t, ndim=1] floats = np.empty(n, dtype='f8')
2213+
ndarray[complex128_t, ndim=1] complexes = np.empty(n, dtype='c16')
2214+
ndarray[int64_t, ndim=1] ints = np.empty(n, dtype='i8')
2215+
ndarray[uint64_t, ndim=1] uints = np.empty(n, dtype='u8')
2216+
ndarray[uint8_t, ndim=1] bools = np.empty(n, dtype='u1')
2217+
ndarray[uint8_t, ndim=1] mask = np.zeros(n, dtype="u1")
22142218
float64_t fval
22152219
bint allow_null_in_int = convert_to_masked_nullable
22162220

pandas/_libs/testing.pyx

+2-3
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@ from numpy cimport import_array
77

88
import_array()
99

10-
from pandas._libs.lib import is_complex
11-
1210
from pandas._libs.util cimport (
1311
is_array,
12+
is_complex_object,
1413
is_real_number_object,
1514
)
1615

@@ -196,7 +195,7 @@ cpdef assert_almost_equal(a, b,
196195
f"with rtol={rtol}, atol={atol}")
197196
return True
198197

199-
if is_complex(a) and is_complex(b):
198+
if is_complex_object(a) and is_complex_object(b):
200199
if array_equivalent(a, b, strict_nan=True):
201200
# inf comparison
202201
return True

0 commit comments

Comments
 (0)