PyCyBase 0.1.10__cp315-cp315-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cbase/__infra__.pxd +6 -0
- cbase/__init__.pxd +6 -0
- cbase/__init__.py +31 -0
- cbase/allocator_protocol/__infra__.pxd +65 -0
- cbase/allocator_protocol/__init__.pxd +65 -0
- cbase/allocator_protocol/__init__.py +45 -0
- cbase/allocator_protocol/c_allocator_protocol.c +31874 -0
- cbase/allocator_protocol/c_allocator_protocol.cp315-win_amd64.pyd +0 -0
- cbase/allocator_protocol/c_allocator_protocol.h +352 -0
- cbase/allocator_protocol/c_allocator_protocol.pxd +66 -0
- cbase/allocator_protocol/c_allocator_protocol.pyi +144 -0
- cbase/allocator_protocol/c_allocator_protocol.pyx +209 -0
- cbase/allocator_protocol/c_heap_allocator.c +38225 -0
- cbase/allocator_protocol/c_heap_allocator.cp315-win_amd64.pyd +0 -0
- cbase/allocator_protocol/c_heap_allocator.h +554 -0
- cbase/allocator_protocol/c_heap_allocator.pxd +105 -0
- cbase/allocator_protocol/c_heap_allocator.pyi +161 -0
- cbase/allocator_protocol/c_heap_allocator.pyx +303 -0
- cbase/allocator_protocol/c_nt_shm_allocator.c +33455 -0
- cbase/allocator_protocol/c_nt_shm_allocator.cp315-win_amd64.pyd +0 -0
- cbase/allocator_protocol/c_nt_shm_allocator.h +1105 -0
- cbase/allocator_protocol/c_nt_shm_allocator.pxd +110 -0
- cbase/allocator_protocol/c_nt_shm_allocator.pyx +223 -0
- cbase/allocator_protocol/c_shm_allocator.h +1270 -0
- cbase/allocator_protocol/c_shm_allocator.pxd +150 -0
- cbase/allocator_protocol/c_shm_allocator.pyi +299 -0
- cbase/allocator_protocol/c_shm_allocator.pyx +445 -0
- cbase/allocator_protocol/c_shm_comp.c +4654 -0
- cbase/allocator_protocol/c_shm_comp.cp315-win_amd64.pyd +0 -0
- cbase/allocator_protocol/c_shm_comp.h +49 -0
- cbase/allocator_protocol/c_shm_comp.pxd +46 -0
- cbase/allocator_protocol/c_shm_comp.pyx +1 -0
- cbase/backports/__infra__.pxd +17 -0
- cbase/backports/__init__.pxd +17 -0
- cbase/backports/__init__.py +9 -0
- cbase/backports/pydict.h +55 -0
- cbase/backports/pydict.pxd +5 -0
- cbase/backports/pylong.c +9965 -0
- cbase/backports/pylong.cp315-win_amd64.pyd +0 -0
- cbase/backports/pylong.h +79 -0
- cbase/backports/pylong.pxd +23 -0
- cbase/backports/pylong.pyx +61 -0
- cbase/bytemap/__infra__.pxd +42 -0
- cbase/bytemap/__init__.pxd +42 -0
- cbase/bytemap/__init__.py +21 -0
- cbase/bytemap/c_bytemap.c +52151 -0
- cbase/bytemap/c_bytemap.cp315-win_amd64.pyd +0 -0
- cbase/bytemap/c_bytemap.h +867 -0
- cbase/bytemap/c_bytemap.pxd +370 -0
- cbase/bytemap/c_bytemap.pyx +1760 -0
- cbase/bytemap/xxh3.h +55 -0
- cbase/bytemap/xxhash.c +42 -0
- cbase/bytemap/xxhash.h +7487 -0
- cbase/config_view.c +7253 -0
- cbase/config_view.cp315-win_amd64.pyd +0 -0
- cbase/config_view.pyx +85 -0
- cbase/env.c +11120 -0
- cbase/env.cp315-win_amd64.pyd +0 -0
- cbase/env.pxd +8 -0
- cbase/env.pyi +48 -0
- cbase/env.pyx +46 -0
- cbase/includes/cbase/allocator_protocol/c_allocator_protocol.c +31874 -0
- cbase/includes/cbase/allocator_protocol/c_allocator_protocol.h +352 -0
- cbase/includes/cbase/allocator_protocol/c_heap_allocator.c +38225 -0
- cbase/includes/cbase/allocator_protocol/c_heap_allocator.h +554 -0
- cbase/includes/cbase/allocator_protocol/c_nt_shm_allocator.c +33455 -0
- cbase/includes/cbase/allocator_protocol/c_nt_shm_allocator.h +1105 -0
- cbase/includes/cbase/allocator_protocol/c_shm_allocator.h +1270 -0
- cbase/includes/cbase/allocator_protocol/c_shm_comp.c +4654 -0
- cbase/includes/cbase/allocator_protocol/c_shm_comp.h +49 -0
- cbase/includes/cbase/backports/pydict.h +55 -0
- cbase/includes/cbase/backports/pylong.c +9965 -0
- cbase/includes/cbase/backports/pylong.h +79 -0
- cbase/includes/cbase/bytemap/c_bytemap.c +52151 -0
- cbase/includes/cbase/bytemap/c_bytemap.h +867 -0
- cbase/includes/cbase/bytemap/xxh3.h +55 -0
- cbase/includes/cbase/bytemap/xxhash.c +42 -0
- cbase/includes/cbase/bytemap/xxhash.h +7487 -0
- cbase/includes/cbase/config_view.c +7253 -0
- cbase/includes/cbase/env.c +11120 -0
- cbase/includes/cbase/int128.h +81 -0
- cbase/includes/cbase/intern_string/c_intern_string.c +28138 -0
- cbase/includes/cbase/intern_string/c_intern_string.h +614 -0
- cbase/includes/cbase/nt/pthread_nt_compat.h +102 -0
- cbase/includes/cbase/nt/regex_nt_compat.h +133 -0
- cbase/includes/cbase/nt/time_sched_nt_compat.h +32 -0
- cbase/int128.h +81 -0
- cbase/int128.pxd +15 -0
- cbase/intern_string/BENCHMARK.md +157 -0
- cbase/intern_string/__infra__.pxd +13 -0
- cbase/intern_string/__init__.pxd +13 -0
- cbase/intern_string/__init__.py +19 -0
- cbase/intern_string/c_intern_string.c +28138 -0
- cbase/intern_string/c_intern_string.cp315-win_amd64.pyd +0 -0
- cbase/intern_string/c_intern_string.h +614 -0
- cbase/intern_string/c_intern_string.pxd +121 -0
- cbase/intern_string/c_intern_string.pyi +376 -0
- cbase/intern_string/c_intern_string.pyx +765 -0
- cbase/nt/pthread_nt_compat.h +102 -0
- cbase/nt/regex_nt_compat.h +133 -0
- cbase/nt/time_sched_nt_compat.h +32 -0
- pycybase-0.1.10.dist-info/METADATA +190 -0
- pycybase-0.1.10.dist-info/RECORD +105 -0
- pycybase-0.1.10.dist-info/WHEEL +5 -0
- pycybase-0.1.10.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,765 @@
|
|
|
1
|
+
from cpython.unicode cimport PyUnicode_AsUTF8AndSize, PyUnicode_FromString
|
|
2
|
+
from libc.stdint cimport uintptr_t
|
|
3
|
+
from libc.stdlib cimport free, calloc
|
|
4
|
+
|
|
5
|
+
from cbase.allocator_protocol.c_allocator_protocol cimport AP_DEFAULT_ALLOCATOR, AP_SHM_ALLOCATOR, AP_HEAP_ALLOCATOR
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
cdef class InternString:
|
|
9
|
+
@staticmethod
|
|
10
|
+
cdef InternString c_from_entry(const istr_entry* entry, InternStringPool parent_pool):
|
|
11
|
+
cdef InternString instance = InternString.__new__(InternString)
|
|
12
|
+
instance.pool = parent_pool
|
|
13
|
+
instance.key = <const char*> entry.key
|
|
14
|
+
instance.hash = entry.hash
|
|
15
|
+
return instance
|
|
16
|
+
|
|
17
|
+
def __gt__(self, object other):
|
|
18
|
+
if isinstance(other, InternString):
|
|
19
|
+
return self.string.__gt__(other.string)
|
|
20
|
+
return self.string.__gt__(other)
|
|
21
|
+
|
|
22
|
+
def __eq__(self, object other):
|
|
23
|
+
if isinstance(other, InternString):
|
|
24
|
+
return self.string.__eq__(other.string)
|
|
25
|
+
return self.string.__eq__(other)
|
|
26
|
+
|
|
27
|
+
def __hash__(self):
|
|
28
|
+
# Truncate to signed 64-bit range for Cython's tp_hash slot.
|
|
29
|
+
# Cython extension types route __hash__ through the C-level
|
|
30
|
+
# Py_hash_t slot which requires Py_ssize_t compatibility.
|
|
31
|
+
# The raw uint64_t hash can overflow this; masking keeps the
|
|
32
|
+
# low 63 bits, which is sufficient for hash tables and
|
|
33
|
+
# matches Python's own hash() truncation behaviour.
|
|
34
|
+
return self.hash & 0x7FFFFFFFFFFFFFFF
|
|
35
|
+
|
|
36
|
+
def __repr__(self):
|
|
37
|
+
if self.key:
|
|
38
|
+
return f'<{self.__class__.__name__}>({PyUnicode_FromString(self.key)})>'
|
|
39
|
+
return f'<{self.__class__.__name__}>(uninitialized)>'
|
|
40
|
+
|
|
41
|
+
property string:
|
|
42
|
+
def __get__(self):
|
|
43
|
+
if self.key:
|
|
44
|
+
return PyUnicode_FromString(self.key)
|
|
45
|
+
raise RuntimeError('Not initialized')
|
|
46
|
+
|
|
47
|
+
property hash_value:
|
|
48
|
+
def __get__(self):
|
|
49
|
+
if self.hash:
|
|
50
|
+
return self.hash
|
|
51
|
+
|
|
52
|
+
property address:
|
|
53
|
+
def __get__(self):
|
|
54
|
+
if self.key:
|
|
55
|
+
return f'{<uintptr_t> self.key:#0x}'
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
cdef class InternStringPool:
|
|
60
|
+
def __init__(self):
|
|
61
|
+
self.pool = c_istr_map_new(0, AP_DEFAULT_ALLOCATOR)
|
|
62
|
+
self.owner = True
|
|
63
|
+
|
|
64
|
+
def __dealloc__(self):
|
|
65
|
+
if not self.owner:
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
if self.pool:
|
|
69
|
+
c_istr_map_free(self.pool)
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
cdef InternStringPool c_from_header(const istr_map* header, bint owner=False):
|
|
73
|
+
cdef InternStringPool instance = InternStringPool.__new__(InternStringPool)
|
|
74
|
+
instance.pool = <istr_map*> header
|
|
75
|
+
instance.owner = owner
|
|
76
|
+
return instance
|
|
77
|
+
|
|
78
|
+
def __len__(self):
|
|
79
|
+
if not self.pool:
|
|
80
|
+
raise RuntimeError(f'<{self.__class__.__name__}> Not properly initialized! Missing istr_map header!')
|
|
81
|
+
return istr_map_size(self.pool)
|
|
82
|
+
|
|
83
|
+
def __getitem__(self, str key):
|
|
84
|
+
cdef Py_ssize_t key_length = 0
|
|
85
|
+
cdef const char* utf8_string = PyUnicode_AsUTF8AndSize(key, &key_length)
|
|
86
|
+
cdef const istr_entry* entry = c_istr_map_lookup_synced(self.pool, utf8_string, <size_t> key_length)
|
|
87
|
+
if not entry:
|
|
88
|
+
raise KeyError(f'{key} not interned')
|
|
89
|
+
return InternString.c_from_entry(entry, self)
|
|
90
|
+
|
|
91
|
+
def __contains__(self, str key):
|
|
92
|
+
cdef Py_ssize_t key_length = 0
|
|
93
|
+
cdef const char* utf8_string = PyUnicode_AsUTF8AndSize(key, &key_length)
|
|
94
|
+
cdef const istr_entry* entry = c_istr_map_lookup_synced(self.pool, utf8_string, <size_t> key_length)
|
|
95
|
+
return entry != NULL
|
|
96
|
+
|
|
97
|
+
cpdef InternString istr(self, str string):
|
|
98
|
+
cdef Py_ssize_t key_length = 0
|
|
99
|
+
cdef const char* utf8_string = PyUnicode_AsUTF8AndSize(string, &key_length)
|
|
100
|
+
cdef const istr_entry* out_entry = NULL
|
|
101
|
+
cdef const char* istr = c_istr_synced(self.pool, utf8_string, <size_t> key_length, &out_entry)
|
|
102
|
+
if not istr:
|
|
103
|
+
raise MemoryError('Failed to intern string.')
|
|
104
|
+
return InternString.c_from_entry(out_entry, self)
|
|
105
|
+
|
|
106
|
+
def internalized(self):
|
|
107
|
+
cdef istr_entry* entry = istr_map_first(self.pool)
|
|
108
|
+
while entry:
|
|
109
|
+
yield InternString.c_from_entry(entry, self)
|
|
110
|
+
entry = entry.next
|
|
111
|
+
|
|
112
|
+
property size:
|
|
113
|
+
def __get__(self):
|
|
114
|
+
if self.pool:
|
|
115
|
+
return istr_map_size(self.pool)
|
|
116
|
+
raise RuntimeError('Not initialized')
|
|
117
|
+
|
|
118
|
+
property address:
|
|
119
|
+
def __get__(self):
|
|
120
|
+
if self.pool:
|
|
121
|
+
return f'{<uintptr_t> self.pool:#0x}'
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
cdef class IstrTestToolkit:
|
|
126
|
+
"""Performance benchmark toolkit for InternString vs Python str.
|
|
127
|
+
|
|
128
|
+
Generates a large shared character buffer, segments it into random-length
|
|
129
|
+
strings, then benchmarks C-level hash / intern / lookup / equality against
|
|
130
|
+
equivalent Python str operations.
|
|
131
|
+
|
|
132
|
+
Parameters
|
|
133
|
+
----------
|
|
134
|
+
buf_size : size_t
|
|
135
|
+
Size of the character buffer in bytes (default 2^30 ≈ 1 GiB).
|
|
136
|
+
n_seg : size_t
|
|
137
|
+
Number of segments to generate (default 100_000).
|
|
138
|
+
max_seg_len : size_t
|
|
139
|
+
Maximum length of each random segment in bytes (default 64).
|
|
140
|
+
n_iters : size_t
|
|
141
|
+
Number of benchmark iterations (default 10).
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
def __cinit__(
|
|
145
|
+
self,
|
|
146
|
+
size_t buf_size=2**30,
|
|
147
|
+
size_t n_seg=100_000,
|
|
148
|
+
size_t max_seg_len=64,
|
|
149
|
+
size_t n_iters=10,
|
|
150
|
+
size_t n_unique=0,
|
|
151
|
+
size_t n_ops=0
|
|
152
|
+
):
|
|
153
|
+
from random import seed, randint
|
|
154
|
+
cdef size_t i
|
|
155
|
+
|
|
156
|
+
if max_seg_len < 1 or max_seg_len > buf_size // n_seg:
|
|
157
|
+
raise ValueError(f'Invalid max_seg_len={max_seg_len} for buf_size={buf_size}, n_seg={n_seg}')
|
|
158
|
+
if n_seg == 0 or buf_size == 0 or n_iters == 0:
|
|
159
|
+
raise ValueError('buf_size, n_seg, n_iters must be > 0')
|
|
160
|
+
if n_unique == 0:
|
|
161
|
+
n_unique = n_seg // 10
|
|
162
|
+
if n_unique < 1:
|
|
163
|
+
n_unique = 1
|
|
164
|
+
|
|
165
|
+
self.buf_size = buf_size
|
|
166
|
+
self.n_seg = n_seg
|
|
167
|
+
self.max_seg_len = max_seg_len
|
|
168
|
+
self.n_iters = n_iters
|
|
169
|
+
self.n_unique = n_unique
|
|
170
|
+
self.n_ops = n_ops if n_ops > 0 else n_seg
|
|
171
|
+
if self.n_unique > n_seg:
|
|
172
|
+
raise ValueError(f'n_unique={n_unique} must be <= n_seg={n_seg}')
|
|
173
|
+
|
|
174
|
+
# Allocate the character buffer (1 extra byte for NUL safety)
|
|
175
|
+
self.buf = <char*> calloc(buf_size + 1, 1)
|
|
176
|
+
if self.buf == NULL:
|
|
177
|
+
raise MemoryError(f'Failed to allocate buf of size {buf_size}')
|
|
178
|
+
|
|
179
|
+
# Allocate segment arrays
|
|
180
|
+
self.seg_offsets = <size_t*> calloc(n_seg, sizeof(size_t))
|
|
181
|
+
self.seg_lengths = <size_t*> calloc(n_seg, sizeof(size_t))
|
|
182
|
+
self.c_keys = <const char**> calloc(n_seg, sizeof(const char*))
|
|
183
|
+
self.c_key_lens = <size_t*> calloc(n_seg, sizeof(size_t))
|
|
184
|
+
# Shuffle indices for limited-pool benchmarks
|
|
185
|
+
self.shuffle_indices = <size_t*> calloc(self.n_ops, sizeof(size_t))
|
|
186
|
+
if self.shuffle_indices == NULL:
|
|
187
|
+
raise MemoryError(f'Failed to allocate shuffle indices for n_ops={self.n_ops}')
|
|
188
|
+
|
|
189
|
+
if (self.seg_offsets == NULL or self.seg_lengths == NULL or
|
|
190
|
+
self.c_keys == NULL or self.c_key_lens == NULL):
|
|
191
|
+
raise MemoryError(f'Failed to allocate segment arrays for n_seg={n_seg}')
|
|
192
|
+
|
|
193
|
+
# Fill buffer and generate segments
|
|
194
|
+
seed(42) # reproducible
|
|
195
|
+
self.c_gen_buf()
|
|
196
|
+
self.c_gen_segments()
|
|
197
|
+
self.c_gen_shuffle()
|
|
198
|
+
|
|
199
|
+
# Prepare Python objects and intern pool
|
|
200
|
+
self.py_strings = []
|
|
201
|
+
self.py_bytes_list = []
|
|
202
|
+
self.pool = InternStringPool()
|
|
203
|
+
self.c_prepare_py_objects()
|
|
204
|
+
self.c_prepare_istr_pool()
|
|
205
|
+
|
|
206
|
+
def __dealloc__(self):
|
|
207
|
+
if self.buf != NULL:
|
|
208
|
+
free(self.buf)
|
|
209
|
+
self.buf = NULL
|
|
210
|
+
if self.seg_offsets != NULL:
|
|
211
|
+
free(self.seg_offsets)
|
|
212
|
+
self.seg_offsets = NULL
|
|
213
|
+
if self.seg_lengths != NULL:
|
|
214
|
+
free(self.seg_lengths)
|
|
215
|
+
self.seg_lengths = NULL
|
|
216
|
+
if self.c_keys != NULL:
|
|
217
|
+
free(self.c_keys)
|
|
218
|
+
self.c_keys = NULL
|
|
219
|
+
if self.c_key_lens != NULL:
|
|
220
|
+
free(self.c_key_lens)
|
|
221
|
+
self.c_key_lens = NULL
|
|
222
|
+
if self.shuffle_indices != NULL:
|
|
223
|
+
free(self.shuffle_indices)
|
|
224
|
+
self.shuffle_indices = NULL
|
|
225
|
+
|
|
226
|
+
# ------------------------------------------------------------------
|
|
227
|
+
# Setup helpers
|
|
228
|
+
# ------------------------------------------------------------------
|
|
229
|
+
|
|
230
|
+
cdef inline void c_gen_buf(self):
|
|
231
|
+
"""Fill buf with random printable ASCII characters."""
|
|
232
|
+
from random import randint
|
|
233
|
+
cdef size_t i
|
|
234
|
+
for i in range(self.buf_size):
|
|
235
|
+
self.buf[i] = <char> randint(33, 126) # printable ASCII
|
|
236
|
+
|
|
237
|
+
cdef inline void c_gen_segments(self):
|
|
238
|
+
"""Generate random non-overlapping offsets and lengths within buf."""
|
|
239
|
+
from random import randint
|
|
240
|
+
cdef size_t i, offset, length
|
|
241
|
+
cdef size_t stride = self.buf_size // self.n_seg
|
|
242
|
+
cdef size_t half_stride = stride // 2
|
|
243
|
+
cdef size_t half_max = self.max_seg_len // 2
|
|
244
|
+
if half_max < 1:
|
|
245
|
+
half_max = 1
|
|
246
|
+
|
|
247
|
+
for i in range(self.n_seg):
|
|
248
|
+
# Base position within the i-th stride
|
|
249
|
+
offset = i * stride
|
|
250
|
+
# Add random jitter so segments aren't perfectly aligned
|
|
251
|
+
offset += <size_t> randint(1, <int> half_stride)
|
|
252
|
+
if offset + self.max_seg_len > self.buf_size:
|
|
253
|
+
offset = self.buf_size - self.max_seg_len
|
|
254
|
+
length = <size_t> randint(1, <int> self.max_seg_len)
|
|
255
|
+
self.seg_offsets[i] = offset
|
|
256
|
+
self.seg_lengths[i] = length
|
|
257
|
+
self.c_keys[i] = self.buf + offset
|
|
258
|
+
self.c_key_lens[i] = length
|
|
259
|
+
|
|
260
|
+
cdef inline void c_prepare_py_objects(self):
|
|
261
|
+
"""Create Python str and bytes objects from every segment."""
|
|
262
|
+
cdef size_t i
|
|
263
|
+
cdef bytes b
|
|
264
|
+
self.py_strings = []
|
|
265
|
+
self.py_bytes_list = []
|
|
266
|
+
for i in range(self.n_seg):
|
|
267
|
+
b = self.buf[self.seg_offsets[i]:self.seg_offsets[i] + self.seg_lengths[i]]
|
|
268
|
+
self.py_bytes_list.append(b)
|
|
269
|
+
self.py_strings.append(b.decode('ascii'))
|
|
270
|
+
|
|
271
|
+
cdef inline void c_prepare_istr_pool(self):
|
|
272
|
+
"""Intern every segment into the pool via Python str (warm-up)."""
|
|
273
|
+
cdef size_t i
|
|
274
|
+
for i in range(self.n_seg):
|
|
275
|
+
self.pool.istr(self.py_strings[i])
|
|
276
|
+
|
|
277
|
+
# ------------------------------------------------------------------
|
|
278
|
+
# C-level hash benchmark (fnv1a_hash)
|
|
279
|
+
# ------------------------------------------------------------------
|
|
280
|
+
|
|
281
|
+
cpdef double istr_hash_routine(self):
|
|
282
|
+
cdef size_t iter_idx, i
|
|
283
|
+
cdef double elapsed = 0.0
|
|
284
|
+
cdef double start_ts
|
|
285
|
+
cdef uint64_t checksum = 0
|
|
286
|
+
|
|
287
|
+
from time import perf_counter
|
|
288
|
+
for iter_idx in range(self.n_iters):
|
|
289
|
+
start_ts = perf_counter()
|
|
290
|
+
for i in range(self.n_seg):
|
|
291
|
+
checksum += fnv1a_hash(self.c_keys[i], self.c_key_lens[i])
|
|
292
|
+
elapsed += perf_counter() - start_ts
|
|
293
|
+
|
|
294
|
+
self.buf[0] = <char> (checksum & 0xFF)
|
|
295
|
+
return elapsed
|
|
296
|
+
|
|
297
|
+
# ------------------------------------------------------------------
|
|
298
|
+
# Intern benchmark — pure C-level, unlocked
|
|
299
|
+
# ------------------------------------------------------------------
|
|
300
|
+
|
|
301
|
+
cpdef double istr_intern_routine(self):
|
|
302
|
+
cdef size_t iter_idx, i
|
|
303
|
+
cdef double elapsed = 0.0
|
|
304
|
+
cdef double start_ts
|
|
305
|
+
cdef istr_map* cmap
|
|
306
|
+
cdef const istr_entry* entry = NULL
|
|
307
|
+
cdef uintptr_t checksum = 0
|
|
308
|
+
|
|
309
|
+
from time import perf_counter
|
|
310
|
+
for iter_idx in range(self.n_iters):
|
|
311
|
+
cmap = c_istr_map_new(0, AP_DEFAULT_ALLOCATOR)
|
|
312
|
+
if cmap == NULL:
|
|
313
|
+
raise MemoryError('c_istr_map_new failed')
|
|
314
|
+
start_ts = perf_counter()
|
|
315
|
+
for i in range(self.n_seg):
|
|
316
|
+
c_istr(cmap, self.c_keys[i], self.c_key_lens[i], &entry)
|
|
317
|
+
checksum += <uintptr_t> entry
|
|
318
|
+
elapsed += perf_counter() - start_ts
|
|
319
|
+
c_istr_map_free(cmap)
|
|
320
|
+
|
|
321
|
+
self.buf[0] = <char> (checksum & 0xFF)
|
|
322
|
+
return elapsed
|
|
323
|
+
|
|
324
|
+
# ------------------------------------------------------------------
|
|
325
|
+
# Intern benchmark — C-level with mutex (synced)
|
|
326
|
+
# ------------------------------------------------------------------
|
|
327
|
+
|
|
328
|
+
cpdef double istr_intern_synced_routine(self):
|
|
329
|
+
cdef size_t iter_idx, i
|
|
330
|
+
cdef double elapsed = 0.0
|
|
331
|
+
cdef double start_ts
|
|
332
|
+
cdef istr_map* cmap
|
|
333
|
+
cdef const istr_entry* entry = NULL
|
|
334
|
+
cdef uintptr_t checksum = 0
|
|
335
|
+
|
|
336
|
+
from time import perf_counter
|
|
337
|
+
for iter_idx in range(self.n_iters):
|
|
338
|
+
cmap = c_istr_map_new(0, AP_DEFAULT_ALLOCATOR)
|
|
339
|
+
if cmap == NULL:
|
|
340
|
+
raise MemoryError('c_istr_map_new failed')
|
|
341
|
+
start_ts = perf_counter()
|
|
342
|
+
for i in range(self.n_seg):
|
|
343
|
+
c_istr_synced(cmap, self.c_keys[i], self.c_key_lens[i], &entry)
|
|
344
|
+
checksum += <uintptr_t> entry
|
|
345
|
+
elapsed += perf_counter() - start_ts
|
|
346
|
+
c_istr_map_free(cmap)
|
|
347
|
+
|
|
348
|
+
self.buf[0] = <char> (checksum & 0xFF)
|
|
349
|
+
return elapsed
|
|
350
|
+
|
|
351
|
+
# ------------------------------------------------------------------
|
|
352
|
+
# Lookup benchmark — pure C-level, unlocked
|
|
353
|
+
# ------------------------------------------------------------------
|
|
354
|
+
|
|
355
|
+
cpdef double istr_lookup_routine(self):
|
|
356
|
+
cdef size_t iter_idx, i
|
|
357
|
+
cdef double elapsed = 0.0
|
|
358
|
+
cdef double start_ts
|
|
359
|
+
cdef const istr_entry* entry = NULL
|
|
360
|
+
cdef uintptr_t checksum = 0
|
|
361
|
+
|
|
362
|
+
from time import perf_counter
|
|
363
|
+
for iter_idx in range(self.n_iters):
|
|
364
|
+
start_ts = perf_counter()
|
|
365
|
+
for i in range(self.n_seg):
|
|
366
|
+
entry = c_istr_map_lookup(self.pool.pool, self.c_keys[i], self.c_key_lens[i])
|
|
367
|
+
checksum += <uintptr_t> entry
|
|
368
|
+
elapsed += perf_counter() - start_ts
|
|
369
|
+
|
|
370
|
+
self.buf[0] = <char> (checksum & 0xFF)
|
|
371
|
+
return elapsed
|
|
372
|
+
|
|
373
|
+
# ------------------------------------------------------------------
|
|
374
|
+
# Lookup benchmark — C-level with mutex (synced)
|
|
375
|
+
# ------------------------------------------------------------------
|
|
376
|
+
|
|
377
|
+
cpdef double istr_lookup_synced_routine(self):
|
|
378
|
+
cdef size_t iter_idx, i
|
|
379
|
+
cdef double elapsed = 0.0
|
|
380
|
+
cdef double start_ts
|
|
381
|
+
cdef const istr_entry* entry = NULL
|
|
382
|
+
cdef uintptr_t checksum = 0
|
|
383
|
+
|
|
384
|
+
from time import perf_counter
|
|
385
|
+
for iter_idx in range(self.n_iters):
|
|
386
|
+
start_ts = perf_counter()
|
|
387
|
+
for i in range(self.n_seg):
|
|
388
|
+
entry = c_istr_map_lookup_synced(self.pool.pool, self.c_keys[i], self.c_key_lens[i])
|
|
389
|
+
checksum += <uintptr_t> entry
|
|
390
|
+
elapsed += perf_counter() - start_ts
|
|
391
|
+
|
|
392
|
+
self.buf[0] = <char> (checksum & 0xFF)
|
|
393
|
+
return elapsed
|
|
394
|
+
|
|
395
|
+
# ------------------------------------------------------------------
|
|
396
|
+
# Equality benchmark — InternString Python-level eq
|
|
397
|
+
# ------------------------------------------------------------------
|
|
398
|
+
|
|
399
|
+
cpdef double istr_eq_routine(self):
|
|
400
|
+
cdef size_t iter_idx, i
|
|
401
|
+
cdef double elapsed = 0.0
|
|
402
|
+
cdef double start_ts
|
|
403
|
+
cdef size_t checksum = 0
|
|
404
|
+
cdef InternString a, b
|
|
405
|
+
|
|
406
|
+
from time import perf_counter
|
|
407
|
+
for iter_idx in range(self.n_iters):
|
|
408
|
+
start_ts = perf_counter()
|
|
409
|
+
for i in range(self.n_seg - 1):
|
|
410
|
+
a = self.pool[self.py_strings[i]]
|
|
411
|
+
b = self.pool[self.py_strings[i + 1]]
|
|
412
|
+
if a == b:
|
|
413
|
+
checksum += 1
|
|
414
|
+
else:
|
|
415
|
+
checksum += 2
|
|
416
|
+
elapsed += perf_counter() - start_ts
|
|
417
|
+
|
|
418
|
+
self.buf[0] = <char> (checksum & 0xFF)
|
|
419
|
+
return elapsed
|
|
420
|
+
|
|
421
|
+
# ------------------------------------------------------------------
|
|
422
|
+
# Python str creation benchmark
|
|
423
|
+
# ------------------------------------------------------------------
|
|
424
|
+
|
|
425
|
+
cpdef double py_unicode_routine(self):
|
|
426
|
+
cdef size_t iter_idx, i
|
|
427
|
+
cdef double elapsed = 0.0
|
|
428
|
+
cdef double start_ts
|
|
429
|
+
cdef uintptr_t checksum = 0
|
|
430
|
+
cdef bytes b
|
|
431
|
+
cdef str s
|
|
432
|
+
|
|
433
|
+
from time import perf_counter
|
|
434
|
+
for iter_idx in range(self.n_iters):
|
|
435
|
+
start_ts = perf_counter()
|
|
436
|
+
for i in range(self.n_seg):
|
|
437
|
+
b = self.buf[self.seg_offsets[i]:self.seg_offsets[i] + self.seg_lengths[i]]
|
|
438
|
+
s = b.decode('ascii')
|
|
439
|
+
checksum += len(s)
|
|
440
|
+
elapsed += perf_counter() - start_ts
|
|
441
|
+
|
|
442
|
+
self.buf[0] = <char> (checksum & 0xFF)
|
|
443
|
+
return elapsed
|
|
444
|
+
|
|
445
|
+
# ------------------------------------------------------------------
|
|
446
|
+
# Python hash benchmark
|
|
447
|
+
# ------------------------------------------------------------------
|
|
448
|
+
|
|
449
|
+
cpdef double py_hash_routine(self):
|
|
450
|
+
cdef size_t iter_idx, i
|
|
451
|
+
cdef double elapsed = 0.0
|
|
452
|
+
cdef double start_ts
|
|
453
|
+
cdef uint64_t checksum = 0
|
|
454
|
+
|
|
455
|
+
from time import perf_counter
|
|
456
|
+
for iter_idx in range(self.n_iters):
|
|
457
|
+
start_ts = perf_counter()
|
|
458
|
+
for i in range(self.n_seg):
|
|
459
|
+
checksum += <uint64_t> hash(self.py_strings[i])
|
|
460
|
+
elapsed += perf_counter() - start_ts
|
|
461
|
+
|
|
462
|
+
self.buf[0] = <char> (checksum & 0xFF)
|
|
463
|
+
return elapsed
|
|
464
|
+
|
|
465
|
+
# ------------------------------------------------------------------
|
|
466
|
+
# Python equality benchmark
|
|
467
|
+
# ------------------------------------------------------------------
|
|
468
|
+
|
|
469
|
+
cpdef double py_eq_routine(self):
|
|
470
|
+
cdef size_t iter_idx, i
|
|
471
|
+
cdef double elapsed = 0.0
|
|
472
|
+
cdef double start_ts
|
|
473
|
+
cdef size_t checksum = 0
|
|
474
|
+
|
|
475
|
+
from time import perf_counter
|
|
476
|
+
for iter_idx in range(self.n_iters):
|
|
477
|
+
start_ts = perf_counter()
|
|
478
|
+
for i in range(self.n_seg - 1):
|
|
479
|
+
if self.py_strings[i] == self.py_strings[i + 1]:
|
|
480
|
+
checksum += 1
|
|
481
|
+
else:
|
|
482
|
+
checksum += 2
|
|
483
|
+
elapsed += perf_counter() - start_ts
|
|
484
|
+
|
|
485
|
+
self.buf[0] = <char> (checksum & 0xFF)
|
|
486
|
+
return elapsed
|
|
487
|
+
|
|
488
|
+
# ------------------------------------------------------------------
|
|
489
|
+
# Shuffle generator — random picks from limited unique pool
|
|
490
|
+
# ------------------------------------------------------------------
|
|
491
|
+
|
|
492
|
+
cdef inline void c_gen_shuffle(self):
|
|
493
|
+
"""Fill shuffle_indices with random picks from [0, n_unique)."""
|
|
494
|
+
from random import randint
|
|
495
|
+
cdef size_t i
|
|
496
|
+
for i in range(self.n_ops):
|
|
497
|
+
self.shuffle_indices[i] = <size_t> randint(0, <int> self.n_unique - 1)
|
|
498
|
+
|
|
499
|
+
# ------------------------------------------------------------------
|
|
500
|
+
# Limited-pool intern — realistic workload (hits + misses mixed)
|
|
501
|
+
# ------------------------------------------------------------------
|
|
502
|
+
|
|
503
|
+
cpdef double istr_limited_pool_routine(self):
|
|
504
|
+
"""Repeatedly intern from a limited pool of n_unique strings.
|
|
505
|
+
|
|
506
|
+
Each iteration picks n_ops random strings (with replacement) from
|
|
507
|
+
the first n_unique segments and interns them. Since the pool is
|
|
508
|
+
small relative to n_ops, most operations are hits (already-interned
|
|
509
|
+
keys) — this mirrors real-world usage where a finite set of strings
|
|
510
|
+
(e.g. ticker symbols) is interned repeatedly.
|
|
511
|
+
"""
|
|
512
|
+
cdef size_t iter_idx, i, idx
|
|
513
|
+
cdef double elapsed = 0.0
|
|
514
|
+
cdef double start_ts
|
|
515
|
+
cdef istr_map* cmap
|
|
516
|
+
cdef const istr_entry* entry = NULL
|
|
517
|
+
cdef uintptr_t checksum = 0
|
|
518
|
+
|
|
519
|
+
from time import perf_counter
|
|
520
|
+
for iter_idx in range(self.n_iters):
|
|
521
|
+
cmap = c_istr_map_new(0, AP_DEFAULT_ALLOCATOR)
|
|
522
|
+
if cmap == NULL:
|
|
523
|
+
raise MemoryError('c_istr_map_new failed')
|
|
524
|
+
start_ts = perf_counter()
|
|
525
|
+
for i in range(self.n_ops):
|
|
526
|
+
idx = self.shuffle_indices[i]
|
|
527
|
+
c_istr(cmap, self.c_keys[idx], self.c_key_lens[idx], &entry)
|
|
528
|
+
checksum += <uintptr_t> entry
|
|
529
|
+
elapsed += perf_counter() - start_ts
|
|
530
|
+
c_istr_map_free(cmap)
|
|
531
|
+
|
|
532
|
+
self.buf[0] = <char> (checksum & 0xFF)
|
|
533
|
+
return elapsed
|
|
534
|
+
|
|
535
|
+
cpdef double istr_limited_pool_synced_routine(self):
|
|
536
|
+
"""Same as istr_limited_pool_routine but with c_istr_synced."""
|
|
537
|
+
cdef size_t iter_idx, i, idx
|
|
538
|
+
cdef double elapsed = 0.0
|
|
539
|
+
cdef double start_ts
|
|
540
|
+
cdef istr_map* cmap
|
|
541
|
+
cdef const istr_entry* entry = NULL
|
|
542
|
+
cdef uintptr_t checksum = 0
|
|
543
|
+
|
|
544
|
+
from time import perf_counter
|
|
545
|
+
for iter_idx in range(self.n_iters):
|
|
546
|
+
cmap = c_istr_map_new(0, AP_DEFAULT_ALLOCATOR)
|
|
547
|
+
if cmap == NULL:
|
|
548
|
+
raise MemoryError('c_istr_map_new failed')
|
|
549
|
+
start_ts = perf_counter()
|
|
550
|
+
for i in range(self.n_ops):
|
|
551
|
+
idx = self.shuffle_indices[i]
|
|
552
|
+
c_istr_synced(cmap, self.c_keys[idx], self.c_key_lens[idx], &entry)
|
|
553
|
+
checksum += <uintptr_t> entry
|
|
554
|
+
elapsed += perf_counter() - start_ts
|
|
555
|
+
c_istr_map_free(cmap)
|
|
556
|
+
|
|
557
|
+
self.buf[0] = <char> (checksum & 0xFF)
|
|
558
|
+
return elapsed
|
|
559
|
+
|
|
560
|
+
# ------------------------------------------------------------------
|
|
561
|
+
# Python unicode creation from limited pool
|
|
562
|
+
# ------------------------------------------------------------------
|
|
563
|
+
|
|
564
|
+
cpdef double py_unicode_limited_routine(self):
|
|
565
|
+
"""Create Python str objects from random limited-pool picks."""
|
|
566
|
+
cdef size_t iter_idx, i, idx
|
|
567
|
+
cdef double elapsed = 0.0
|
|
568
|
+
cdef double start_ts
|
|
569
|
+
cdef bytes b
|
|
570
|
+
cdef str s
|
|
571
|
+
cdef uintptr_t checksum = 0
|
|
572
|
+
|
|
573
|
+
from time import perf_counter
|
|
574
|
+
for iter_idx in range(self.n_iters):
|
|
575
|
+
start_ts = perf_counter()
|
|
576
|
+
for i in range(self.n_ops):
|
|
577
|
+
idx = self.shuffle_indices[i]
|
|
578
|
+
b = self.buf[self.seg_offsets[idx]:self.seg_offsets[idx] + self.seg_lengths[idx]]
|
|
579
|
+
s = b.decode('ascii')
|
|
580
|
+
checksum += len(s)
|
|
581
|
+
elapsed += perf_counter() - start_ts
|
|
582
|
+
|
|
583
|
+
self.buf[0] = <char> (checksum & 0xFF)
|
|
584
|
+
return elapsed
|
|
585
|
+
|
|
586
|
+
# ------------------------------------------------------------------
|
|
587
|
+
# Miss-rate controlled benchmarks
|
|
588
|
+
# ------------------------------------------------------------------
|
|
589
|
+
|
|
590
|
+
cdef inline void c_gen_miss_pattern(self, double miss_rate):
|
|
591
|
+
"""Fill shuffle_indices with a pattern achieving the target miss rate.
|
|
592
|
+
|
|
593
|
+
Misses (new unique strings) are evenly distributed at interval
|
|
594
|
+
ceil(1/miss_rate). Between misses, random picks from the
|
|
595
|
+
already-seen set simulate hits.
|
|
596
|
+
"""
|
|
597
|
+
from random import randint
|
|
598
|
+
cdef size_t i, seq_ptr, miss_interval
|
|
599
|
+
cdef size_t n_misses
|
|
600
|
+
|
|
601
|
+
if miss_rate <= 0.0 or miss_rate > 1.0:
|
|
602
|
+
raise ValueError(f'miss_rate must be in (0, 1], got {miss_rate}')
|
|
603
|
+
|
|
604
|
+
n_misses = <size_t> (self.n_ops * miss_rate)
|
|
605
|
+
if n_misses < 1:
|
|
606
|
+
n_misses = 1
|
|
607
|
+
if n_misses > self.n_seg:
|
|
608
|
+
raise ValueError(f'n_misses={n_misses} exceeds n_seg={self.n_seg}')
|
|
609
|
+
|
|
610
|
+
miss_interval = self.n_ops // n_misses
|
|
611
|
+
if miss_interval < 1:
|
|
612
|
+
miss_interval = 1
|
|
613
|
+
|
|
614
|
+
seq_ptr = 0
|
|
615
|
+
for i in range(self.n_ops):
|
|
616
|
+
if i % miss_interval == 0 and seq_ptr < n_misses:
|
|
617
|
+
# MISS — use next unique segment
|
|
618
|
+
self.shuffle_indices[i] = seq_ptr
|
|
619
|
+
seq_ptr += 1
|
|
620
|
+
else:
|
|
621
|
+
# HIT — random pick from already-seen set
|
|
622
|
+
if seq_ptr > 0:
|
|
623
|
+
self.shuffle_indices[i] = <size_t> randint(0, <int> seq_ptr - 1)
|
|
624
|
+
else:
|
|
625
|
+
self.shuffle_indices[i] = 0
|
|
626
|
+
|
|
627
|
+
cpdef double istr_miss_rate_routine(self, double miss_rate):
|
|
628
|
+
"""Benchmark intern with a controlled miss rate.
|
|
629
|
+
|
|
630
|
+
miss_rate=0.001 → 1/1K, miss_rate=0.0001 → 1/10K, etc.
|
|
631
|
+
"""
|
|
632
|
+
cdef size_t iter_idx, i, idx
|
|
633
|
+
cdef double elapsed = 0.0
|
|
634
|
+
cdef double start_ts
|
|
635
|
+
cdef istr_map* cmap
|
|
636
|
+
cdef const istr_entry* entry = NULL
|
|
637
|
+
cdef uintptr_t checksum = 0
|
|
638
|
+
|
|
639
|
+
self.c_gen_miss_pattern(miss_rate)
|
|
640
|
+
|
|
641
|
+
from time import perf_counter
|
|
642
|
+
for iter_idx in range(self.n_iters):
|
|
643
|
+
cmap = c_istr_map_new(0, AP_DEFAULT_ALLOCATOR)
|
|
644
|
+
if cmap == NULL:
|
|
645
|
+
raise MemoryError('c_istr_map_new failed')
|
|
646
|
+
start_ts = perf_counter()
|
|
647
|
+
for i in range(self.n_ops):
|
|
648
|
+
idx = self.shuffle_indices[i]
|
|
649
|
+
c_istr(cmap, self.c_keys[idx], self.c_key_lens[idx], &entry)
|
|
650
|
+
checksum += <uintptr_t> entry
|
|
651
|
+
elapsed += perf_counter() - start_ts
|
|
652
|
+
c_istr_map_free(cmap)
|
|
653
|
+
|
|
654
|
+
self.buf[0] = <char> (checksum & 0xFF)
|
|
655
|
+
return elapsed
|
|
656
|
+
|
|
657
|
+
cpdef double py_unicode_miss_rate_routine(self, double miss_rate):
|
|
658
|
+
"""Python str creation with the same miss-rate pattern."""
|
|
659
|
+
cdef size_t iter_idx, i, idx
|
|
660
|
+
cdef double elapsed = 0.0
|
|
661
|
+
cdef double start_ts
|
|
662
|
+
cdef bytes b
|
|
663
|
+
cdef str s
|
|
664
|
+
cdef uintptr_t checksum = 0
|
|
665
|
+
|
|
666
|
+
self.c_gen_miss_pattern(miss_rate)
|
|
667
|
+
|
|
668
|
+
from time import perf_counter
|
|
669
|
+
for iter_idx in range(self.n_iters):
|
|
670
|
+
start_ts = perf_counter()
|
|
671
|
+
for i in range(self.n_ops):
|
|
672
|
+
idx = self.shuffle_indices[i]
|
|
673
|
+
b = self.buf[self.seg_offsets[idx]:self.seg_offsets[idx] + self.seg_lengths[idx]]
|
|
674
|
+
s = b.decode('ascii')
|
|
675
|
+
checksum += len(s)
|
|
676
|
+
elapsed += perf_counter() - start_ts
|
|
677
|
+
|
|
678
|
+
self.buf[0] = <char> (checksum & 0xFF)
|
|
679
|
+
return elapsed
|
|
680
|
+
|
|
681
|
+
# ------------------------------------------------------------------
|
|
682
|
+
# Run all benchmarks
|
|
683
|
+
# ------------------------------------------------------------------
|
|
684
|
+
|
|
685
|
+
cpdef dict run_test(self):
|
|
686
|
+
cdef double istr_hash_t = self.istr_hash_routine()
|
|
687
|
+
cdef double istr_intern_t = self.istr_intern_routine()
|
|
688
|
+
cdef double istr_intern_s_t = self.istr_intern_synced_routine()
|
|
689
|
+
cdef double istr_lookup_t = self.istr_lookup_routine()
|
|
690
|
+
cdef double istr_lookup_s_t = self.istr_lookup_synced_routine()
|
|
691
|
+
cdef double istr_limited_t = self.istr_limited_pool_routine()
|
|
692
|
+
cdef double istr_limited_s_t = self.istr_limited_pool_synced_routine()
|
|
693
|
+
cdef double istr_eq_t = self.istr_eq_routine()
|
|
694
|
+
cdef double py_unicode_t = self.py_unicode_routine()
|
|
695
|
+
cdef double py_unicode_lim_t = self.py_unicode_limited_routine()
|
|
696
|
+
cdef double py_hash_t = self.py_hash_routine()
|
|
697
|
+
cdef double py_eq_t = self.py_eq_routine()
|
|
698
|
+
|
|
699
|
+
cdef size_t total_bytes = 0
|
|
700
|
+
cdef size_t i
|
|
701
|
+
for i in range(self.n_seg):
|
|
702
|
+
total_bytes += self.seg_lengths[i]
|
|
703
|
+
|
|
704
|
+
cdef size_t n = self.n_seg
|
|
705
|
+
cdef size_t n_iters = self.n_iters
|
|
706
|
+
cdef size_t n_ops = self.n_ops
|
|
707
|
+
|
|
708
|
+
return {
|
|
709
|
+
'buf_size': self.buf_size,
|
|
710
|
+
'n_seg': n,
|
|
711
|
+
'n_unique': self.n_unique,
|
|
712
|
+
'n_ops': n_ops,
|
|
713
|
+
'max_seg_len': self.max_seg_len,
|
|
714
|
+
'n_iters': n_iters,
|
|
715
|
+
'total_key_bytes': total_bytes,
|
|
716
|
+
# Intern string — C-level all-miss (fresh pool each iter)
|
|
717
|
+
'istr_hash_ns': (istr_hash_t / n_iters / n) * 1e9,
|
|
718
|
+
'istr_intern_ns': (istr_intern_t / n_iters / n) * 1e9,
|
|
719
|
+
'istr_intern_synced_ns':(istr_intern_s_t / n_iters / n) * 1e9,
|
|
720
|
+
# Intern string — C-level lookup (all hits)
|
|
721
|
+
'istr_lookup_ns': (istr_lookup_t / n_iters / n) * 1e9,
|
|
722
|
+
'istr_lookup_synced_ns':(istr_lookup_s_t / n_iters / n) * 1e9,
|
|
723
|
+
# Intern string — limited pool (realistic hit/miss mix)
|
|
724
|
+
'istr_limited_ns': (istr_limited_t / n_iters / n_ops) * 1e9,
|
|
725
|
+
'istr_limited_synced_ns': (istr_limited_s_t / n_iters / n_ops) * 1e9,
|
|
726
|
+
'istr_limited_mutex_ns': ((istr_limited_s_t - istr_limited_t) / n_iters / n_ops) * 1e9,
|
|
727
|
+
# Mutex overhead (all-miss)
|
|
728
|
+
'intern_mutex_ns': ((istr_intern_s_t - istr_intern_t) / n_iters / n) * 1e9,
|
|
729
|
+
'lookup_mutex_ns': ((istr_lookup_s_t - istr_lookup_t) / n_iters / n) * 1e9,
|
|
730
|
+
# Equality
|
|
731
|
+
'istr_eq_ns': (istr_eq_t / n_iters / (n - 1)) * 1e9,
|
|
732
|
+
# Python benchmarks
|
|
733
|
+
'py_unicode_ns': (py_unicode_t / n_iters / n) * 1e9,
|
|
734
|
+
'py_unicode_limited_ns':(py_unicode_lim_t / n_iters / n_ops) * 1e9,
|
|
735
|
+
'py_hash_ns': (py_hash_t / n_iters / n) * 1e9,
|
|
736
|
+
'py_eq_ns': (py_eq_t / n_iters / (n - 1)) * 1e9,
|
|
737
|
+
# Raw timings
|
|
738
|
+
'istr_hash_s': istr_hash_t,
|
|
739
|
+
'istr_intern_s': istr_intern_t,
|
|
740
|
+
'istr_intern_synced_s': istr_intern_s_t,
|
|
741
|
+
'istr_lookup_s': istr_lookup_t,
|
|
742
|
+
'istr_lookup_synced_s': istr_lookup_s_t,
|
|
743
|
+
'istr_limited_s': istr_limited_t,
|
|
744
|
+
'istr_limited_synced_s': istr_limited_s_t,
|
|
745
|
+
'istr_eq_s': istr_eq_t,
|
|
746
|
+
'py_unicode_s': py_unicode_t,
|
|
747
|
+
'py_unicode_limited_s': py_unicode_lim_t,
|
|
748
|
+
'py_hash_s': py_hash_t,
|
|
749
|
+
'py_eq_s': py_eq_t,
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
# ============================================================
|
|
754
|
+
# Module-level pools
|
|
755
|
+
# ============================================================
|
|
756
|
+
|
|
757
|
+
cdef istr_map* C_POOL = c_istr_map_new(0, AP_SHM_ALLOCATOR)
|
|
758
|
+
globals()['C_POOL'] = <uintptr_t> C_POOL
|
|
759
|
+
cdef InternStringPool POOL = InternStringPool.c_from_header(C_POOL, True)
|
|
760
|
+
globals()['POOL'] = POOL
|
|
761
|
+
|
|
762
|
+
cdef istr_map* C_INTRA_POOL = c_istr_map_new(0, AP_HEAP_ALLOCATOR)
|
|
763
|
+
globals()['C_INTRA_POOL'] = <uintptr_t> C_INTRA_POOL
|
|
764
|
+
cdef InternStringPool INTRA_POOL = InternStringPool.c_from_header(C_INTRA_POOL, True)
|
|
765
|
+
globals()['INTRA_POOL'] = INTRA_POOL
|