Skip to content

Commit 207d4dd

Browse files
authored
Merge branch 'main' into copilot/use-stats-to-create-filters
2 parents 6304a3a + ae2d31b commit 207d4dd

50 files changed

Lines changed: 2454 additions & 205 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,4 @@ tests/draken/performance/.perf_baseline.json
219219
opteryx/draken/vectors/_hash_api.c*
220220
third_party/mabel/draken/vectors/interval_vector.c*
221221
third_party/mabel/draken/morsels/align.c*
222+
third_party/mabel/draken/compiled/maskops.cpp

opteryx/__version__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# THIS FILE IS AUTOMATICALLY UPDATED DURING THE BUILD PROCESS
22
# DO NOT EDIT THIS FILE DIRECTLY
33

4-
__build__ = 1907
4+
__build__ = 1926
55
__author__ = "@joocer"
6-
__version__ = "0.26.2-beta.1907"
6+
__version__ = "0.26.2-beta.1926"
77

88
# Store the version here so:
99
# 1) we don't load dependencies by storing it in __init__.py

opteryx/compiled/aggregations/group_by_draken.pyx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ cpdef Morsel group_by_morsel(
5151
object iter_indices
5252
int64_t group_val_int
5353
double group_val_double
54+
uint64_t h
55+
set seen_set
5456

5557
if morsel is None or morsel.num_rows == 0:
5658
# return empty table similar to what pyarrow would return
@@ -85,7 +87,6 @@ cpdef Morsel group_by_morsel(
8587

8688
# Build map of hash -> vector of row indices
8789
# Store seen keys in seen_keys (Python list) to iterate later
88-
cdef uint64_t h
8990
for i in range(num_rows):
9091
h = <uint64_t> row_hashes[i]
9192
fmap.insert(h, i)
@@ -98,7 +99,7 @@ cpdef Morsel group_by_morsel(
9899

99100
# Build unique keys dict by iterating again and filling a Python dict
100101
seen_keys = []
101-
cdef set seen_set = set()
102+
seen_set = set()
102103
for i in range(num_rows):
103104
h = <uint64_t> row_hashes[i]
104105
if h not in seen_set:

opteryx/compiled/functions/strings.pyx

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# cython: boundscheck=False
88

99
import platform
10+
from libcpp.vector cimport vector
1011

1112
cdef extern from "simd_search.h":
1213
size_t neon_count(const char* data, size_t length, char target)
@@ -15,21 +16,30 @@ cdef extern from "simd_search.h":
1516
int avx_search(const char* data, size_t length, char target)
1617
int neon_find_delimiter(const char* data, size_t length)
1718
int avx_find_delimiter(const char* data, size_t length)
19+
vector[size_t] neon_find_all(const char* data, size_t length, char target)
20+
vector[size_t] avx_find_all(const char* data, size_t length, char target)
21+
22+
cdef extern from "simd_string_ops.h":
23+
void simd_to_upper(char* data, size_t length)
24+
void simd_to_lower(char* data, size_t length)
1825

1926
# Architecture detection
2027
cdef size_t (*simd_count)(const char*, size_t, char)
2128
cdef int (*simd_search)(const char*, size_t, char)
2229
cdef int (*simd_find_delimiter)(const char*, size_t)
30+
cdef vector[size_t] (*simd_find_all)(const char*, size_t, char)
2331

2432
_arch = platform.machine().lower()
2533
if _arch in ('arm64', 'aarch64'):
2634
simd_count = neon_count
2735
simd_search = neon_search
2836
simd_find_delimiter = neon_find_delimiter
37+
simd_find_all = neon_find_all
2938
else:
3039
simd_count = avx_count
3140
simd_search = avx_search
3241
simd_find_delimiter = avx_find_delimiter
42+
simd_find_all = avx_find_all
3343

3444

3545
# Expose simd_count to Python so higher-level code can reuse the optimized newline/count routine.
@@ -43,3 +53,70 @@ cpdef size_t count_instances(const unsigned char[::1] mv, char target=10):
4353
cdef const char* data = <const char*>udata
4454
cdef size_t data_len = mv.shape[0]
4555
return simd_count(data, data_len, target)
56+
57+
58+
# String case conversion functions
59+
def to_upper(bytes data):
60+
"""
61+
Convert ASCII characters in bytes to uppercase using SIMD.
62+
Non-ASCII bytes are left unchanged.
63+
64+
Returns a new bytes object with uppercase characters.
65+
"""
66+
cdef size_t length = len(data)
67+
cdef bytearray result = bytearray(data)
68+
cdef char* ptr = <char*> result
69+
simd_to_upper(ptr, length)
70+
return bytes(result)
71+
72+
73+
def to_lower(bytes data):
74+
"""
75+
Convert ASCII characters in bytes to lowercase using SIMD.
76+
Non-ASCII bytes are left unchanged.
77+
78+
Returns a new bytes object with lowercase characters.
79+
"""
80+
cdef size_t length = len(data)
81+
cdef bytearray result = bytearray(data)
82+
cdef char* ptr = <char*> result
83+
simd_to_lower(ptr, length)
84+
return bytes(result)
85+
86+
87+
# Character search functions
88+
def find_char(bytes data, int target):
89+
"""
90+
Find the first occurrence of a character in bytes using SIMD.
91+
92+
Returns the index of the first occurrence, or -1 if not found.
93+
"""
94+
cdef const char* ptr = <const char*> (<char*> data)
95+
cdef size_t length = len(data)
96+
cdef char char_target = <char> target
97+
return simd_search(ptr, length, char_target)
98+
99+
100+
def count_char(bytes data, int target):
101+
"""
102+
Count occurrences of a character in bytes using SIMD.
103+
104+
Returns the count of occurrences.
105+
"""
106+
cdef const char* ptr = <const char*> (<char*> data)
107+
cdef size_t length = len(data)
108+
cdef char char_target = <char> target
109+
return simd_count(ptr, length, char_target)
110+
111+
112+
def find_all_char(bytes data, int target):
113+
"""
114+
Find all occurrences of a character in bytes using SIMD.
115+
116+
Returns a list of indices where the character occurs.
117+
"""
118+
cdef const char* ptr = <const char*> (<char*> data)
119+
cdef size_t length = len(data)
120+
cdef char char_target = <char> target
121+
cdef vector[size_t] positions = simd_find_all(ptr, length, char_target)
122+
return list(positions)

opteryx/compiled/list_ops/list_initcap.pyx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@ cdef inline numpy.ndarray[object, ndim=1] _ensure_object_array(object data):
2222
return data
2323
return data.astype(object)
2424

25-
if hasattr(data, "combine_chunks"):
26-
data = data.combine_chunks()
25+
if hasattr(data, "chunks"):
26+
# Handle ChunkedArray by processing chunks individually to avoid massive copy
27+
return numpy.concatenate([_ensure_object_array(chunk) for chunk in data.chunks])
28+
29+
# if hasattr(data, "combine_chunks"):
30+
# data = data.combine_chunks()
2731

2832
if hasattr(data, "to_numpy"):
2933
arr = data.to_numpy(zero_copy_only=False)

opteryx/compiled/list_ops/list_length.pyx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ cpdef numpy.ndarray[numpy.uint32_t, ndim=1] list_length(object array):
2626
# PyArrow fast path (uses offsets buffer)
2727
if isinstance(array, (pyarrow.Array, pyarrow.ChunkedArray, pyarrow.lib.StringArray)):
2828
if isinstance(array, pyarrow.ChunkedArray):
29-
array = array.combine_chunks()
29+
return numpy.concatenate([list_length(chunk) for chunk in array.chunks])
3030

3131
n = len(array)
3232

opteryx/compiled/structures/buffers.pxd

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import numpy
1010
cimport numpy
1111

12-
from libc.stdint cimport int64_t
12+
from libc.stdint cimport int64_t, int32_t
1313
from libc.stddef cimport size_t
1414
from libcpp.vector cimport vector
1515

@@ -20,14 +20,29 @@ cdef extern from "intbuffer.h" namespace "" nogil:
2020
void extend(const vector[int64_t]& values)
2121
void extend(const int64_t* values, size_t count)
2222
void reserve(size_t additional_capacity)
23+
void resize(size_t new_size)
2324
const int64_t* data() const
25+
int64_t* mutable_data()
2426
size_t size() const
2527
void append_repeated(int64_t value, size_t count)
2628

29+
cdef cppclass CInt32Buffer:
30+
CInt32Buffer(size_t size_hint) except +
31+
void append(int32_t value)
32+
void extend(const vector[int32_t]& values)
33+
void extend(const int32_t* values, size_t count)
34+
void reserve(size_t additional_capacity)
35+
void resize(size_t new_size)
36+
const int32_t* data() const
37+
int32_t* mutable_data()
38+
size_t size() const
39+
2740

2841
cdef class IntBuffer:
2942

3043
cdef CIntBuffer* c_buffer
44+
cdef Py_ssize_t _shape[1]
45+
cdef Py_ssize_t _strides[1]
3146

3247
cpdef void append(self, int64_t value)
3348
cpdef void extend(self, iterable)
@@ -37,3 +52,16 @@ cdef class IntBuffer:
3752
cpdef void extend_numpy(self, numpy.ndarray[int64_t, ndim=1] arr)
3853
cpdef void reserve(self, size_t capacity)
3954
cpdef void append_batch(self, int64_t[::1] values)
55+
56+
cdef class Int32Buffer:
57+
58+
cdef CInt32Buffer* c_buffer
59+
cdef Py_ssize_t _shape[1]
60+
cdef Py_ssize_t _strides[1]
61+
62+
cpdef void append(self, int32_t value)
63+
cpdef void extend(self, iterable)
64+
cpdef numpy.ndarray[int32_t, ndim=1] to_numpy(self)
65+
cpdef size_t size(self)
66+
cpdef void reserve(self, size_t capacity)
67+
cpdef void extend_numpy(self, numpy.ndarray[int32_t, ndim=1] arr)

0 commit comments

Comments
 (0)