Skip to content

Commit 213ade1

Browse files
WillAydpmhatre1
authored andcommitted
Use memcpy / realloc more effectively in hashtable (pandas-dev#57695)
1 parent c8a4567 commit 213ade1

File tree

4 files changed

+35
-22
lines changed

4 files changed

+35
-22
lines changed

pandas/_libs/hashtable.pxd

+1-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ cdef class Int64Vector(Vector):
183183
cdef Int64VectorData data
184184
cdef ndarray ao
185185

186-
cdef resize(self)
186+
cdef resize(self, Py_ssize_t new_size)
187187
cpdef ndarray to_array(self)
188188
cdef void append(self, int64_t x) noexcept
189189
cdef extend(self, int64_t[:] x)

pandas/_libs/hashtable.pyx

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ from libc.stdlib cimport (
77
free,
88
malloc,
99
)
10+
from libc.string cimport memcpy
1011

1112
import numpy as np
1213

pandas/_libs/hashtable_class_helper.pxi.in

+31-19
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,9 @@ ctypedef fused vector_data:
163163
Complex64VectorData
164164
StringVectorData
165165

166-
cdef bint needs_resize(vector_data *data) noexcept nogil:
167-
return data.size == data.capacity
166+
167+
cdef bint needs_resize(Py_ssize_t nelems, Py_ssize_t capacity) noexcept nogil:
168+
return nelems >= capacity
168169

169170
# ----------------------------------------------------------------------
170171
# Vector
@@ -214,8 +215,8 @@ cdef class {{name}}Vector(Vector):
214215
self.ao = np.empty(self.data.capacity, dtype=np.{{dtype}})
215216
self.data.data = <{{c_type}}*>self.ao.data
216217

217-
cdef resize(self):
218-
self.data.capacity = max(self.data.capacity * 4, _INIT_VEC_CAP)
218+
cdef resize(self, Py_ssize_t new_size):
219+
self.data.capacity = max(new_size, _INIT_VEC_CAP)
219220
self.ao.resize(self.data.capacity, refcheck=False)
220221
self.data.data = <{{c_type}}*>self.ao.data
221222

@@ -234,17 +235,28 @@ cdef class {{name}}Vector(Vector):
234235

235236
cdef void append(self, {{c_type}} x) noexcept:
236237

237-
if needs_resize(&self.data):
238+
if needs_resize(self.data.size, self.data.capacity):
238239
if self.external_view_exists:
239240
raise ValueError("external reference but "
240241
"Vector.resize() needed")
241-
self.resize()
242+
self.resize(self.data.capacity * 4)
242243

243244
append_data_{{dtype}}(&self.data, x)
244245

245246
cdef extend(self, const {{c_type}}[:] x):
246-
for i in range(len(x)):
247-
self.append(x[i])
247+
cdef Py_ssize_t x_size = len(x)
248+
if x_size == 0:
249+
return
250+
251+
cdef Py_ssize_t needed_size = self.data.size + x_size
252+
if needs_resize(needed_size, self.data.capacity):
253+
if self.external_view_exists:
254+
raise ValueError("external reference but "
255+
"Vector.resize() needed")
256+
self.resize(needed_size)
257+
258+
memcpy(self.data.data + self.data.size, &x[0], x_size * sizeof({{c_type}}))
259+
self.data.size = needed_size
248260

249261
{{endfor}}
250262

@@ -260,7 +272,7 @@ cdef class StringVector(Vector):
260272
if self.data.data is NULL:
261273
raise MemoryError()
262274

263-
cdef resize(self):
275+
cdef resize(self, Py_ssize_t new_size):
264276
cdef:
265277
char **orig_data
266278
Py_ssize_t i, orig_capacity
@@ -297,8 +309,8 @@ cdef class StringVector(Vector):
297309

298310
cdef void append(self, char *x) noexcept:
299311

300-
if needs_resize(&self.data):
301-
self.resize()
312+
if needs_resize(self.data.size, self.data.capacity):
313+
self.resize(self.data.capacity * 4)
302314

303315
append_data_string(&self.data, x)
304316

@@ -684,18 +696,18 @@ cdef class {{name}}HashTable(HashTable):
684696
continue
685697

686698
seen_na = True
687-
if needs_resize(ud):
699+
if needs_resize(ud.size, ud.capacity):
688700
with gil:
689701
if uniques.external_view_exists:
690702
raise ValueError("external reference to "
691703
"uniques held, but "
692704
"Vector.resize() needed")
693-
uniques.resize()
705+
uniques.resize(uniques.data.capacity * 4)
694706
if result_mask.external_view_exists:
695707
raise ValueError("external reference to "
696708
"result_mask held, but "
697709
"Vector.resize() needed")
698-
result_mask.resize()
710+
result_mask.resize(result_mask.data.capacity * 4)
699711
append_data_{{dtype}}(ud, val)
700712
append_data_uint8(rmd, 1)
701713
continue
@@ -706,19 +718,19 @@ cdef class {{name}}HashTable(HashTable):
706718
# k hasn't been seen yet
707719
k = kh_put_{{dtype}}(self.table, val, &ret)
708720

709-
if needs_resize(ud):
721+
if needs_resize(ud.size, ud.capacity):
710722
with gil:
711723
if uniques.external_view_exists:
712724
raise ValueError("external reference to "
713725
"uniques held, but "
714726
"Vector.resize() needed")
715-
uniques.resize()
727+
uniques.resize(uniques.data.capacity * 4)
716728
if use_result_mask:
717729
if result_mask.external_view_exists:
718730
raise ValueError("external reference to "
719731
"result_mask held, but "
720732
"Vector.resize() needed")
721-
result_mask.resize()
733+
result_mask.resize(result_mask.data.capacity * 4)
722734
append_data_{{dtype}}(ud, val)
723735
if use_result_mask:
724736
append_data_uint8(rmd, 0)
@@ -849,9 +861,9 @@ cdef class {{name}}HashTable(HashTable):
849861
k = kh_put_{{dtype}}(self.table, val, &ret)
850862
self.table.vals[k] = count
851863

852-
if needs_resize(ud):
864+
if needs_resize(ud.size, ud.capacity):
853865
with gil:
854-
uniques.resize()
866+
uniques.resize(uniques.data.capacity * 4)
855867
append_data_{{dtype}}(ud, val)
856868
labels[i] = count
857869
count += 1

pandas/_libs/hashtable_func_helper.pxi.in

+2-2
Original file line numberDiff line numberDiff line change
@@ -480,9 +480,9 @@ def _unique_label_indices_{{dtype}}(const {{c_type}}[:] labels) -> ndarray:
480480
for i in range(n):
481481
kh_put_{{ttype}}(table, labels[i], &ret)
482482
if ret != 0:
483-
if needs_resize(ud):
483+
if needs_resize(ud.size, ud.capacity):
484484
with gil:
485-
idx.resize()
485+
idx.resize(idx.data.capacity * 4)
486486
append_data_{{ttype}}(ud, i)
487487

488488
kh_destroy_{{ttype}}(table)

0 commit comments

Comments
 (0)