feat: inline cast uint32 and int16 to speed up unmarshall (#115)

This commit is contained in:
J. Nick Koston 2022-10-27 13:34:32 -05:00 committed by GitHub
parent 5c2826d2f1
commit 24dd9d9742
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 58 additions and 3 deletions

View File

@ -0,0 +1,4 @@
class FakeCython:
@property
def compiled(self):
return False

View File

@ -14,6 +14,8 @@ cdef unsigned int BIG_ENDIAN
cdef unsigned int PROTOCOL_VERSION
cdef str UINT32_CAST
cdef str INT16_CAST
cdef bint SYS_IS_LITTLE_ENDIAN
cdef bint SYS_IS_BIG_ENDIAN
cdef object UNPACK_HEADER_LITTLE_ENDIAN
cdef object UNPACK_HEADER_BIG_ENDIAN
@ -30,6 +32,13 @@ cdef object HEADER_MESSAGE_ARG_NAME
cpdef get_signature_tree
cdef inline unsigned long _cast_uint32_native(const char * payload, unsigned int offset):
cdef unsigned long *u32p = <unsigned long *> &payload[offset]
return u32p[0]
cdef inline short _cast_int16_native(const char * payload, unsigned int offset):
cdef short *s16p = <short *> &payload[offset]
return s16p[0]
cdef class MarshallerStreamEndError(Exception):
pass
@ -49,6 +58,7 @@ cdef class Unmarshaller:
cdef unsigned int _message_type
cdef unsigned int _flag
cdef unsigned int _msg_len
cdef unsigned int _is_native
cdef object _uint32_unpack
cdef object _int16_unpack

View File

@ -1,6 +1,5 @@
import array
import io
import pprint
import socket
import sys
from struct import Struct
@ -24,6 +23,9 @@ INT16_CAST = "h"
INT16_SIZE = 2
INT16_DBUS_TYPE = "n"
SYS_IS_LITTLE_ENDIAN = sys.byteorder == "little"
SYS_IS_BIG_ENDIAN = sys.byteorder == "big"
DBUS_TO_CTYPE = {
"y": ("B", 1), # byte
INT16_DBUS_TYPE: (INT16_CAST, INT16_SIZE), # int16
@ -99,6 +101,11 @@ class MarshallerStreamEndError(Exception):
pass
try:
import cython
except ImportError:
from ._cython_compat import FakeCython as cython
#
# Alignment padding is handled with the following formula below
#
@ -134,6 +141,7 @@ class Unmarshaller:
"_msg_len",
"_uint32_unpack",
"_int16_unpack",
"_is_native",
)
def __init__(self, stream: io.BufferedRWPair, sock: Optional[socket.socket] = None):
@ -150,6 +158,7 @@ class Unmarshaller:
self._message_type = 0
self._flag = 0
self._msg_len = 0
self._is_native = 0
self._uint32_unpack: Callable | None = None
self._int16_unpack: Callable | None = None
@ -168,6 +177,7 @@ class Unmarshaller:
self._message_type = 0
self._flag = 0
self._msg_len = 0
self._is_native = 0
self._uint32_unpack = None
self._int16_unpack = None
@ -230,6 +240,10 @@ class Unmarshaller:
def _read_uint32_unpack(self) -> int:
self._pos += UINT32_SIZE + (-self._pos & (UINT32_SIZE - 1)) # align
if self._is_native and cython.compiled:
return _cast_uint32_native( # pragma: no cover
self._buf, self._pos - UINT32_SIZE
)
return self._uint32_unpack(self._buf, self._pos - UINT32_SIZE)[0]
def read_int16_unpack(self, type_: SignatureType) -> int:
@ -237,6 +251,10 @@ class Unmarshaller:
def _read_int16_unpack(self) -> int:
self._pos += INT16_SIZE + (-self._pos & (INT16_SIZE - 1)) # align
if self._is_native and cython.compiled:
return _cast_int16_native( # pragma: no cover
self._buf, self._pos - INT16_SIZE
)
return self._int16_unpack(self._buf, self._pos - INT16_SIZE)[0]
def read_boolean(self, type_: SignatureType) -> bool:
@ -250,7 +268,12 @@ class Unmarshaller:
self._pos += UINT32_SIZE + (-self._pos & (UINT32_SIZE - 1)) # align
str_start = self._pos
# read terminating '\0' byte as well (str_length + 1)
self._pos += self._uint32_unpack(self._buf, str_start - UINT32_SIZE)[0] + 1
if self._is_native and cython.compiled:
self._pos += ( # pragma: no cover
_cast_uint32_native(self._buf, str_start - UINT32_SIZE) + 1
)
else:
self._pos += self._uint32_unpack(self._buf, str_start - UINT32_SIZE)[0] + 1
return self._buf[str_start : self._pos - 1].decode()
def read_signature(self, type_: SignatureType) -> str:
@ -302,7 +325,12 @@ class Unmarshaller:
self._pos += (
-self._pos & (UINT32_SIZE - 1)
) + UINT32_SIZE # align for the uint32
array_length = self._uint32_unpack(self._buf, self._pos - UINT32_SIZE)[0]
if self._is_native and cython.compiled:
array_length = _cast_uint32_native( # pragma: no cover
self._buf, self._pos - UINT32_SIZE
)
else:
array_length = self._uint32_unpack(self._buf, self._pos - UINT32_SIZE)[0]
child_type = type_.children[0]
token = child_type.token
@ -398,6 +426,14 @@ class Unmarshaller:
f"got unknown protocol version: {protocol_version}"
)
if cython.compiled and (
(endian == LITTLE_ENDIAN and SYS_IS_LITTLE_ENDIAN)
or (endian == BIG_ENDIAN and SYS_IS_BIG_ENDIAN)
):
self._is_native = 1 # pragma: no cover
self._body_len = _cast_uint32_native(self._buf, 4) # pragma: no cover
self._serial = _cast_uint32_native(self._buf, 8) # pragma: no cover
self._header_len = _cast_uint32_native(self._buf, 12) # pragma: no cover
if endian == LITTLE_ENDIAN:
(
self._body_len,

View File

@ -7,6 +7,7 @@ from unittest.mock import patch
import pytest
from dbus_fast import Message, MessageFlag, MessageType, SignatureTree, Variant
from dbus_fast._private._cython_compat import FakeCython
from dbus_fast._private.unmarshaller import Unmarshaller
@ -171,3 +172,7 @@ def test_ay_buffer():
marshalled = msg._marshall(False)
unmarshalled_msg = Unmarshaller(io.BytesIO(marshalled)).unmarshall()
assert unmarshalled_msg.body[0] == body[0]
def tests_fallback_no_cython():
assert FakeCython().compiled is False