Python is one of the most popular and popular programming languages today. However, when working with Python, you will see or hear about one of the weaknesses to this point: Python is slow
.
There are several ways to speed up your Python code. Maybe you read it somewhere:
As above, you will approach 2 sides to speed up Python code: parallel programming
and asynchronous programming
. Now, I will introduce a different approach. That is Cython
.
Can understand Cython is an intermediate step between Python and C / C ++. It allows you to write pure Python with some minor modifications, then translate it directly into C.
Cython will bring you the combined power of Python and C:
Some other information:
You can easily the Cython via pip
pip install cython
Compared to Python code, you need to add type information to every variable. Usually, to declare a Python variable, very simple:
x = 1
With Cython, you need to add the type for that variable:
cdef int x = 1
Just like in C, the type declaration for a variable in Cython is required.
When using Cython, there are two different points for variables and functions.
For variable:
cdef int a, b, c
cdef char *s
cdef float x = 0.5 (single precision)
cdef double x = 60.4 (double precision)
cdef list images
cdef dict user
cdef object card_deck
All of these types are derived from C / C ++.
For function:
def function1...
cdef function2...
cpdef function2...
With:
First, I will create a pure Python code with for-loop.
#run_test_python.py
def test_python(x):
y = 1
for i in range(1, x+1):
y *= i
return y
Applying what I have understood above, I will write the code in Cython with a meaning:
#run_test_cython.pyx
cpdef int test_cython(int x):
cdef int y = 1
cdef int i
for i in range(1, x+1):
y *= i
return y
When coding Cython, make sure that all of your variables are set.
Next, we need to create a file to compile from Cython -> C code:
# setup.py
from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules = cythonize('run_test_cython.pyx'))
After putting run_test_cython.pyx
and setup.py
with dir, we start to compile:
% python setup.py build_ext --inplace
Compiling run_test_cython.pyx because it changed.
[1/1] Cythonizing run_test_cython.pyx
running build_ext
building 'run_test_cython' extension
creating build
creating build/temp.linux-x86_64-3.6
gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -fPIC -I/home/ha.hao.minh/.pyenv/versions/viblo-venv/include -I/home/ha.hao.minh/.pyenv/versions/3.6.8/include/python3.6m -c run_test_cython.c -o build/temp.linux-x86_64-3.6/run_test_cython.o
gcc -pthread -shared -L/home/ha.hao.minh/.pyenv/versions/3.6.8/lib -L/home/ha.hao.minh/.pyenv/versions/3.6.8/lib build/temp.linux-x86_64-3.6/run_test_cython.o -o /home/ha.hao.minh/workspace/viblo/112019/run_test_cython.cpython-36m-x86_64-linux-gnu.so
Result:
% ls
build run_test_cython.c run_test_cython.cpython-36m-x86_64-linux-gnu.so run_test_cython.pyx run_test_python.py setup.py
You will see in this folder that contains all the files needed to run C code. If you’re curious what the other Cython code will compile into is what C code you can cat to see that file:
cat run_test_cython.c
Come on, it’s time to show the power of C code. The following code compares the speed of Python pure and Cython:
# speedtest.py
import run_test_python
import run_test_cython
import time
def speedtest_python(number):
start = time.time()
run_test_python.test_python(number)
end = time.time()
py_time = end - start
print(f"Python time = {py_time}")
return py_time
def speedtest_cython(number):
start = time.time()
run_test_cython.test_cython(number)
end = time.time()
cy_time = end - start
print("Cython time = {}".format(cy_time))
return cy_time
if __name__=="__main__":
for number in [10, 100, 1000, 10000, 100000]:
print(f"Speedtest with number = {number}")
py_time = speedtest_python(number)
cy_time = speedtest_cython(number)
print("Speedup = {}".format(py_time / cy_time))
Results after running speedtest.py
% python speedtest.py
Speedtest with number = 10
Python time = 3.5762786865234375e-06
Cython time = 7.152557373046875e-07
Speedup = 5.0
Speedtest with number = 100
Python time = 7.867813110351562e-06
Cython time = 2.384185791015625e-07
Speedup = 33.0
Speedtest with number = 1000
Python time = 0.0002810955047607422
Cython time = 9.5367431640625e-07
Speedup = 294.75
Speedtest with number = 10000
Python time = 0.02144336700439453
Cython time = 7.62939453125e-06
Speedup = 2810.625
Speedtest with number = 100000
Python time = 3.1171438694000244
Cython time = 8.630752563476562e-05
Speedup = 36116.70994475138
With the current device configuration for my test:
Fill the results in the table for easy viewing:
NumberPython timeCython timeSpeedupten
36116
- Seems like an unthinkable number
Clearly, Cython gives you very good performance. This is also a viable solution if you want to improve your code.
#python #cython