I ran another benchmark showing the opposite case: a single call to a long
running c function. The results are surprising, Cython is much much faster.
(this may be do my inability to compile an optimized dll).
Anyway, the following is the code I tested:
##### Cython script (.pyx) #########
cdef double count(int n) nogil:
cdef int i, j, k
cdef double out
for i from 0 <= i < n:
for j from 0 <= j < n:
out = 0
for k from 0 <= k < n:
out += k
return out
def myinterface(int a):
cdef int n = a
cdef double out
with nogil:
out = count(n)
return out
####### Ctypes scripts (pure .c) #########
#include "ctypesgiltest.h"
EXPORT double count(int n){
int i, j, k;
int a, b, c;
double out;
a = n;
for (i=0; i<a; i++){
b = n;
for (j=0; j<b; j++){
out = 0;
c = n;
for (k=0; k<c; k++){
out += k;
}
}
}
return out;
}
/******** giltest.h ***********/
#ifdef BUILD_DLL
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif
EXPORT double count(int);
####### python script testing the two functions ########
import threading
import time
from giltest import myinterface
from ctypes import *
class NoGil(threading.Thread):
def __init__(self, id):
super(NoGil, self).__init__()
self.id = id
def run(self):
t1 = time.clock()
a = myinterface(2000)
secs = time.clock() - t1
print ('no GIL %s: ' % self.id), ('Value: %f' % a), ('Time: %f s' %
secs)
class NoGilCtypes(threading.Thread):
def __init__(self, id):
super(NoGilCtypes, self).__init__()
self.id = id
self.count = cdll.ctypesgiltest.count
self.count.restype = c_double
def run(self):
t1 = time.clock()
a = self.count(c_int(2000))
secs = time.clock() - t1
print ('no GIL %s: ' % self.id), ('Value: %f' % a), ('Time: %f s' %
secs)
if __name__=='__main__':
a = NoGil('cython')
b = NoGilCtypes('ctypes')
print ('Starting Threads')
a.start()
b.start()
on my machine Q9650 Win Vista, this uses the expected 2 cores during
execution.
The results are:
>>>
Starting Threads
>>> no GIL cython: Value: 1999000.000000 Time: 8.076570 s
no GIL ctypes: Value: 1999000.000000 Time: 38.927630 s
That's a BIG advantage to Cython! Kudos to the developers.
Looking into the Cython generated code, the two c files are virtually
identical. I assume the performance is down to compiler optimization.
Using minGW-32, I issued the following commands to build the DLL for ctypes:
>gcc -c -DBUILD_DLL ctypesgiltest.c
>gcc -shared -o ctypesgiltest.dll ctypesgiltest.o
Chris
On Wed, May 13, 2009 at 4:53 AM, Robert Bradshaw <
[email protected]> wrote:
> On May 13, 2009, at 1:50 AM, Sebastien Binet wrote:
>
> > On Wednesday 13 May 2009 10:41:43 Robert Bradshaw wrote:
> >> On May 13, 2009, at 1:17 AM, Sebastien Binet wrote:
> >>> hi,
> >>>
> >>> On Wednesday 13 May 2009 08:35:27 Robert Bradshaw wrote:
> >>>> On May 12, 2009, at 9:47 PM, Mohamed Lrhazi wrote:
> >>>>> On Wed, May 13, 2009 at 12:31 AM, Chris Colbert
> >>>>>
> >>>>> <[email protected]> wrote:
> >>>>>> If your making lots of rapid calls to short running functions in
> >>>>>> the
> >>>>>> C-library, then you may start to feel the ctypes overhead.
> >>>>>
> >>>>> That's what I was afraid to hear..
> >>>>
> >>>> Hopefully after using Cython a bit, you're fears will quickly go
> >>>> away :).
> >>>>
> >>>>> What I was hoping to hear is "Oh
> >>>>> no, ctypes is all C anyways, and will perform just the same as
> >>>>> Cython"
> >>>>
> >>>> A simple benchmark:
> >>>>
> >>>> import ctypes
> >>>> libm = ctypes.cdll.LoadLibrary("libm.dylib") # platform
> >>>> dependent...
> >>>> def ctypes_sum(N):
> >>>> lib_sqrt = libm.sqrt
> >>>> lib_sqrt.argtypes = (ctypes.c_double,)
> >>>> lib_sqrt.restype = ctypes.c_double
> >>>> s = 0
> >>>> for i in range(N):
> >>>> s += lib_sqrt(i)
> >>>> return s
> >>>>
> >>>> %cython
> >>>> cdef extern from "math.h":
> >>>> double sqrt(double)
> >>>>
> >>>> def cython_sum(long N):
> >>>> cdef int i
> >>>> cdef double s=0
> >>>> for i in range(N):
> >>>> s += sqrt(i)
> >>>> return s
> >>>>
> >>>>>>> time ctypes_sum(10**6)
> >>>>
> >>>> 666666166.4588418
> >>>> Time: CPU 1.13 s, Wall: 1.14 s
> >>>>
> >>>> time cython_sum(10**6)
> >>>> 666666166.4588418
> >>>> Time: CPU 0.03 s, Wall: 0.03 s
> >>>
> >>> interesting simple minded benchmark :)
> >>>
> >>> how would this translate into the pure-python mode ?
> >>
> >> Pure Python:
> >>
> >> def python_sum(N):
> >> from math import sqrt
> >> s = 0
> >> for i in range(N):
> >> s += sqrt(i)
> >> return s
> >>
> >>>>> time python_sum(10**6)
> >>
> >> 666666166.4588418
> >> Time: CPU 0.47 s, Wall: 0.47 s
> >>
> >> So for such a tiny call, ctypes is slower. (Since math.sqrt is a
> >> static wrapper around libm's sqrt, no surprise here.)
> >>
> >>> (I couldn't seem to be
> >>> able to declare the C-sqrt function using the pure-python mode of
> >>> cython:
> >>> http://wiki.cython.org/pure wasn't helpful)
> >>
> >> The pure-python mode should be exactly the same as the above, but
> >> there's no way to declare functions in it (yet).
> > but if you pyximport import it, you should get the performance back
> > (assuming
> > the function declaration were here), right ?
>
> Yes, that's right.
>
> There'd have to be some way of specifying "here's a C function, but
> in pure python mode, use this python function instead."
>
> - Robert
>
> _______________________________________________
> Cython-dev mailing list
> [email protected]
> http://codespeak.net/mailman/listinfo/cython-dev
>
_______________________________________________
Cython-dev mailing list
[email protected]
http://codespeak.net/mailman/listinfo/cython-dev