[Cython] About IndexNode and unicode[index]
Zaur Shibzukhov
szport at gmail.com
Fri Mar 1 08:37:00 CET 2013
2013/3/1 ZS <szport at gmail.com>:
> 2013/3/1 Stefan Behnel <stefan_ml at behnel.de>:
>> ZS, 28.02.2013 21:07:
>>> 2013/2/28 Stefan Behnel:
>>>>> This allows to write unicode text parsing code almost at C speed
>>>>> mostly in python (+ .pxd defintions).
>>>>
>>>> I suggest simply adding a constant flag argument to the existing function
>>>> that states if checking should be done or not. Inlining will let the C
>>>> compiler drop the corresponding code, which may or may nor make it a little
>>>> faster.
>>>
>>> static inline Py_UCS4 unicode_char2(PyObject* ustring, Py_ssize_t i, int flag) {
>>> Py_ssize_t length;
>>> #if CYTHON_PEP393_ENABLED
>>> if (PyUnicode_READY(ustring) < 0) return (Py_UCS4)-1;
>>> #endif
>>> if (flag) {
>>> length = __Pyx_PyUnicode_GET_LENGTH(ustring);
>>> if ((0 <= i) & (i < length)) {
>>> return __Pyx_PyUnicode_READ_CHAR(ustring, i);
>>> } else if ((-length <= i) & (i < 0)) {
>>> return __Pyx_PyUnicode_READ_CHAR(ustring, i + length);
>>> } else {
>>> PyErr_SetString(PyExc_IndexError, "string index out of range");
>>> return (Py_UCS4)-1;
>>> }
>>> } else {
>>> return __Pyx_PyUnicode_READ_CHAR(ustring, i);
>>> }
>>> }
>>
>> I think you could even pass in two flags, one for wraparound and one for
>> boundscheck, and then just evaluate them appropriately in the existing "if"
>> tests above. That should allow both features to be supported independently
>> in a fast way.
>>
>>
>>> Here are timings:
>>>
>>> (py33) zbook:mytests $ python3.3 -m timeit -n 50 -r 5 -s "from
>>> mytests.unicode_index import test_1" "test_1()"
>>> 50 loops, best of 5: 152 msec per loop
>>> (py33) zbook:mytests $ python3.3 -m timeit -n 50 -r 5 -s "from
>>> mytests.unicode_index import test_2" "test_2()"
>>> 50 loops, best of 5: 86.5 msec per loop
>>> (py33) zbook:mytests $ python3.3 -m timeit -n 50 -r 5 -s "from
>>> mytests.unicode_index import test_3" "test_3()"
>>> 50 loops, best of 5: 86.5 msec per loop
>>>
>>> So your suggestion would be preferable.
>>
>> Nice. Yes, looks like it' worth it.
>>
>
> Sure that same could be applied to unicode slicing too.
>
I had to verify myself first. So here is the test...
unicode_slice.h
---------------------
#include "unicodeobject.h"
static inline PyObject* unicode_slice(
PyObject* text, Py_ssize_t start, Py_ssize_t stop);
/////////////// PyUnicode_Substring ///////////////
/* CURRENT */
static inline PyObject* unicode_slice(
PyObject* text, Py_ssize_t start, Py_ssize_t stop) {
Py_ssize_t length;
#if CYTHON_PEP393_ENABLED
if (PyUnicode_READY(text) == -1) return NULL;
length = PyUnicode_GET_LENGTH(text);
#else
length = PyUnicode_GET_SIZE(text);
#endif
if (start < 0) {
start += length;
if (start < 0)
start = 0;
}
if (stop < 0)
stop += length;
else if (stop > length)
stop = length;
length = stop - start;
if (length <= 0)
return PyUnicode_FromUnicode(NULL, 0);
#if CYTHON_PEP393_ENABLED
return PyUnicode_FromKindAndData(PyUnicode_KIND(text),
PyUnicode_1BYTE_DATA(text) + start*PyUnicode_KIND(text), stop-start);
#else
return PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(text)+start, stop-start);
#endif
}
static inline PyObject* unicode_slice2(
PyObject* text, Py_ssize_t start, Py_ssize_t stop, int flag);
/////////////// PyUnicode_Substring ///////////////
/* CHANGED */
static inline PyObject* unicode_slice2(
PyObject* text, Py_ssize_t start, Py_ssize_t stop, int flag) {
Py_ssize_t length;
#if CYTHON_PEP393_ENABLED
if (PyUnicode_READY(text) == -1) return NULL;
#endif
if (flag) {
#if CYTHON_PEP393_ENABLED
length = PyUnicode_GET_LENGTH(text);
#else
length = PyUnicode_GET_SIZE(text);
#endif
if (start < 0) {
start += length;
if (start < 0)
start = 0;
}
if (stop < 0)
stop += length;
else if (stop > length)
stop = length;
length = stop - start;
if (length <= 0)
return PyUnicode_FromUnicode(NULL, 0);
}
#if CYTHON_PEP393_ENABLED
return PyUnicode_FromKindAndData(PyUnicode_KIND(text),
PyUnicode_1BYTE_DATA(text) + start*PyUnicode_KIND(text), stop-start);
#else
return PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(text)+start, stop-start);
#endif
}
unicode_slice.pyx
------------------------
cdef extern from 'unicode_slice.h':
inline unicode unicode_slice(unicode ustring, int start, int stop)
inline unicode unicode_slice2(unicode ustring, int start, int
stop, int flag)
cdef unicode text = u"abcdefghigklmnopqrstuvwxyzabcdefghigklmnopqrstuvwxyz"
cdef long f_1(unicode text):
cdef int i, j
cdef int n = len(text)
cdef int val
cdef long S = 0
for j in range(100000):
for i in range(n):
val = len(unicode_slice(text, 0, i))
S += val * j
return S
cdef long f_2(unicode text):
cdef int i, j
cdef int n = len(text)
cdef int val
cdef long S = 0
for j in range(100000):
for i in range(n):
val = len(unicode_slice2(text, 0, i, 0))
S += val * j
return S
def test_1():
f_1(text)
def test_2():
f_2(text)
Here are timings:
(py33) zbook:mytests $ python3.3 -m timeit -n 50 -r 5 -s "from
mytests.unicode_slice import test_1" "test_1()"
50 loops, best of 5: 534 msec per loop
(py33) zbook:mytests $ python3.3 -m timeit -n 50 -r 5 -s "from
mytests.unicode_slice import test_2" "test_2()"
50 loops, best of 5: 523 msec per loop
Only 2%
Zaur Shibzukhov
More information about the cython-devel
mailing list