From 3c0c8782c945b04d2dc5de899583017cc390af8b Mon Sep 17 00:00:00 2001 From: "Mike C. Fletcher" Date: Sat, 18 Nov 2017 02:43:23 -0500 Subject: [PATCH] Add a cython test to give perspective on relative ease/performance The cython implementation is about 2x slower than the Rust implementation, but it's not clear if the slice in the cython call is actually doing a copy of the entire memory-area or just doing a simple reference of the memory. Note that the cython implementation doesn't build a proper module with setup.py, it just uses the command-line cythonize operation to produce a quick-and-dirty extension. --- .gitignore | 4 ++++ Makefile | 17 ++++++++++++++++- doubles_all.py | 4 ++++ pyext-mycythonlib/mycythonlib.pyx | 18 ++++++++++++++++++ requirements.txt | 1 + 5 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 pyext-mycythonlib/mycythonlib.pyx diff --git a/.gitignore b/.gitignore index 2a62691..0e1b4a7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,12 @@ /pyext-myclib/myclib.py /pyext-myclib/myclib_wrap.c /pyext-myclib/_myclib.cpython* +/pyext-mycythonlib/*.so +/pyext-mycythonlib/*.c +/pyext-mycythonlib/*.html .cache .benchmarks __pycache__ myrustlib.so +mycythonlib*.so /pyext-myrustlib/.gitignore diff --git a/Makefile b/Makefile index 53ae741..457c005 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: clean clean-test clean-pyc clean-build docs help +.PHONY: clean clean-test clean-pyc clean-build docs help test-python test-rust test-c test-cython compile-cython compile-rust compile-c clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts @@ -6,6 +6,7 @@ clean-build: ## remove build artifacts rm -fr build/ rm -fr dist/ rm -fr .eggs/ + rm -fr .compiled find . -name '*.egg-info' -exec rm -fr {} + find . -name '*.egg' -exec rm -f {} + @@ -32,6 +33,9 @@ test-rust: ## run tests quickly with the default Python test-c: ## run tests quickly with the default Python py.test -v -s doubles_with_c_swig.py +test-cython: # run tests quickly with the default Python + py.test -v -s doubles_with_cython.py + test-all: ## run tests quickly with the default Python py.test -v -s doubles_all.py @@ -41,3 +45,14 @@ compile-rust: ## compile new rust lib compile-c: ## compile new c lib @cd pyext-myclib;python3 setup.py build_ext -i + +compile-cython: ## compile new cython lib + @cd pyext-mycythonlib;cythonize -a -i mycythonlib.pyx + @cp pyext-mycythonlib/mycythonlib*.so ./ + +compile-all: compile-rust compile-c compile-cython + +.compiled: compile-all + touch .compiled + +test: compile-all test-all diff --git a/doubles_all.py b/doubles_all.py index 742fd4c..0e46e4e 100644 --- a/doubles_all.py +++ b/doubles_all.py @@ -4,6 +4,7 @@ import itertools import numpy as np import myrustlib # <-- Importing Rust Implemented Library +import mycythonlib # <-- Importing Cython Implemented Library import sys sys.path.append('./pyext-myclib') @@ -101,3 +102,6 @@ def test_c_swig_bytes_once(benchmark): # def test_rust_regex(benchmark): # print(benchmark(myrustlib.count_doubles_regex, val)) + +def test_cython(benchmark): + print(benchmark(mycythonlib.count_doubles, val)) diff --git a/pyext-mycythonlib/mycythonlib.pyx b/pyext-mycythonlib/mycythonlib.pyx new file mode 100644 index 0000000..ebf8baf --- /dev/null +++ b/pyext-mycythonlib/mycythonlib.pyx @@ -0,0 +1,18 @@ +def count_doubles( unicode source ): + """Count number of doubles in a (unicode) string + + A double is counted for every character where the + character at the previous index in the string is + the same character as the current character. Thus + the string 'aaa' has two doubles. + """ + cdef Py_ssize_t count + count = 0 + if not source: + return count + char = source[0] + for next_char in source[1:]: + if next_char == char: + count += 1 + char = next_char + return count diff --git a/requirements.txt b/requirements.txt index 86c9ef6..37738dd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ pytest pytest-benchmark numpy +cython \ No newline at end of file