Skip to content

Instantly share code, notes, and snippets.

@standy66
Created March 28, 2018 21:29
Show Gist options
  • Save standy66/67b174534e500e733fb493ac54a1e11c to your computer and use it in GitHub Desktop.
Save standy66/67b174534e500e733fb493ac54a1e11c to your computer and use it in GitHub Desktop.
PyPy vs CPython extension call overhead
"""
Original code: https://gist.github.com/brentp/7e173302952b210aeaf3
Compare speed of a cython wrapper vs a cffi wrapper to the same underlying
C-code with a fast function and a longer-running function.
This should run anywhere that has cffi and cython installed.
$ python3.6 -V
Python 3.6.2 (default, Jul 17 2017, 16:44:45)
[GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)] on darwin
$ python3.6 compare_wrappers.py
adding 2 numbers with cffi wrapper: 0.299 usec
adding 2 numbers with cython wrapper: 0.242 usec
summing numbers inverses between 1 and 2000 with cffi wrapper: 6.356 usec
summing numbers inverses between 1 and 2000 with cython wrapper: 6.312 usec
$ pypy3 -V
Python 3.5.3 (3f6eaa010fce78cc7973bdc1dfdb95970f08fed2, Jan 13 2018, 18:14:01)
[PyPy 5.10.1 with GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)]
$ pypy3 compare_wrappers.py
adding 2 numbers with cffi wrapper: 0.021 usec
adding 2 numbers with cython wrapper: 2.789 usec
summing numbers inverses between 1 and 2000 with cffi wrapper: 6.021 usec
summing numbers inverses between 1 and 2000 with cython wrapper: 8.376 usec
It seems that
* In PyPy 5.10.1 cpyext (CPython API compatibility layers) has a constant overhead
of ~2.5 usec per call.
* CFFI has zero overhead in PyPy (~20 nsec, comparable to plain C call).
* In CPython 3.6 CFFI and CPython C Extension API has almost identical overhead of
~250 nsec per call, with CFFI lagging a bit behind.
"""
from timeit import timeit
import cffi
import re
import os.path as op
import sys
import time
ffi = cffi.FFI()
ffi.cdef("""
int sum(int, int);
long inv_sum_between(int, int);
""")
# here is the C code that we are wrapping. One quick running function and
# another potentially long-running function.
code = """
int sum(int a, int b){
return a + b;
}
long inv_sum_between(int a, int b){
float sum = 0;
int i;
for(i=a; i<=b; i++) sum += (1.0 / i);
return (long)sum;
}
"""
C = ffi.verify(code)
import pyximport; pyximport.install()
with open('ccy.h', 'w') as fh:
fh.write(code)
# for cython, we also need to write the python callable wrappers.
cycode = """
cdef extern from "%s/ccy.h":
int sum(int a, int b)
long inv_sum_between(int a, int b)
cpdef int pysum(int a, int b):
return sum(a, b)
cpdef long pyinv_sum_between(int a, int b):
return inv_sum_between(a, b)
""" % op.abspath(".")
with open('ccy.pyx', 'w') as fh:
fh.write(cycode)
import ccy
start = time.time()
a = 0
for i in range(1000000):
a = C.sum(a, a)
print("adding 2 numbers with cffi wrapper: {:.3f} usec".format(time.time() - start))
start = time.time()
a = 0
for i in range(1000000):
a = ccy.pysum(a, a)
print("adding 2 numbers with cython wrapper: {:.3f} usec".format(time.time() - start))
start = time.time()
a = 0
for i in range(1000000):
a = C.inv_sum_between(1, 2000 + a % 2)
print("summing numbers inverses between 1 and 2000 with cffi wrapper: {:.3f} usec"
.format(time.time() - start))
start = time.time()
a = 0
for i in range(1000000):
a = ccy.pyinv_sum_between(1, 2000 + a % 2)
print("summing numbers inverses between 1 and 2000 with cython wrapper: {:.3f} usec"
.format(time.time() - start))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment