From 302b29cba420f83215bdc61f41875df467e28743 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Wed, 23 Aug 2017 20:06:48 +0200 Subject: [PATCH] Cythonize ewkb reader and writer --- benchmark/__init__.py | 19 ++++++++++++++ circle.yml | 20 +++++++------- postgis/{ewkb.py => ewkb.pyx} | 49 ++++++++++++++++++++--------------- postgis/geometry.py | 6 ++--- setup.py | 2 ++ 5 files changed, 63 insertions(+), 33 deletions(-) create mode 100644 benchmark/__init__.py rename postgis/{ewkb.py => ewkb.pyx} (73%) diff --git a/benchmark/__init__.py b/benchmark/__init__.py new file mode 100644 index 0000000..e4d19fb --- /dev/null +++ b/benchmark/__init__.py @@ -0,0 +1,19 @@ +from timeit import timeit + +from postgis import Point + + +def main(): + point = Point([1.23456789, 48.765432]) + ewkb = point.to_ewkb() + time = timeit('read(ewkb)', + setup='from postgis.ewkb import read', + number=1000000, globals=locals()) + print("Read Point", time) + time = timeit('point.to_ewkb()', + number=1000000, globals=locals()) + print("Write Point", time) + + +if __name__ == '__main__': + main() diff --git a/circle.yml b/circle.yml index 2a458c6..f6a7094 100644 --- a/circle.yml +++ b/circle.yml @@ -1,16 +1,18 @@ machine: - python: - version: 3.6.1 + python: + version: 3.6.1 database: - pre: - - psql -U ubuntu -c "CREATE DATABASE test;" - - psql -U ubuntu -c "create extension postgis" -d test + pre: + - psql -U ubuntu -c "CREATE DATABASE test;" + - psql -U ubuntu -c "create extension postgis" -d test test: - override: - - py.test tests/ + override: + - py.test tests/ dependencies: - pre: - - pip install pytest psycopg2 asyncpg cython + override: + - python setup.py develop + pre: + - pip install -U pip setuptools pytest psycopg2 asyncpg cython diff --git a/postgis/ewkb.py b/postgis/ewkb.pyx similarity index 73% rename from postgis/ewkb.py rename to postgis/ewkb.pyx index 481c45d..38cc5ff 100644 --- a/postgis/ewkb.py +++ b/postgis/ewkb.pyx @@ -1,6 +1,7 @@ import binascii from io import BytesIO import struct +from cpython cimport bool class Typed(type): @@ -20,17 +21,23 @@ def __call__(cls, *args, **kwargs): return super().__call__(*args, **kwargs) -class Reader: +cdef class Reader: - __slots__ = ['stream', 'endianness', 'has_z', 'has_m'] + cdef object stream + cdef bytes endianness + cdef public bool has_z + cdef public bool has_m - def __init__(self, stream): + def __cinit__(self, object stream): self.stream = stream - def clone(self): + cpdef clone(self): return type(self)(self.stream) - def read(self): + cpdef read(self): + return self._read() + + cdef _read(self): # https://en.wikipedia.org/wiki/Well-known_text#Well-known_binary byte_order = self.stream.read(1) if byte_order == b'\x00': @@ -53,22 +60,22 @@ def read(self): else: return class_.from_ewkb_body(self, srid) - def read_int(self): + cpdef read_int(self): return struct.unpack(self.endianness + b'I', self.stream.read(4))[0] - def read_double(self): + cpdef read_double(self): return struct.unpack(self.endianness + b'd', self.stream.read(8))[0] - @classmethod - def from_hex(cls, value): - return cls(BytesIO(binascii.a2b_hex(value))).read() + +cpdef read(str value): + return Reader(BytesIO(binascii.a2b_hex(value))).read() -class Writer: +cdef class Writer: - __slots__ = ['stream'] + cdef object stream - def __init__(self, geometry, stream=None): + def __cinit__(self, object geometry, object stream=None): self.stream = stream or BytesIO() try: type_ = geometry.TYPE @@ -85,17 +92,17 @@ def __init__(self, geometry, stream=None): if geometry.has_srid: self.write_int(geometry.srid) - def write_int(self, value): + cpdef write_int(self, value): self.stream.write(struct.pack(b'