Brian, in the Cython version, class Neuron, do this: instead of "def update(...)", please write "cpdef update(...)" and time your code again. If this make it faster (it should be near the C-struct version), please consider first removing your original "def update", and replace your "cdef _update()" by "cpdef update()".
On Thu, May 7, 2009 at 8:10 PM, Brian Blais <[email protected]> wrote: > Hello, > I am trying to write a neuron simulator, and I have the need for speed. :) > Unfortunately, I seem to be taking a serious performance hit by writing > things as classes, as opposed to c-structs. I am including the code below > (I hope it's not too much) of the smallest meaningful example I could write > (although it still doesn't do anything real. :) ). The first is an > all-python version, which is easy to follow but slow. > In [1]:%timeit py1.runit() > 10 loops, best of 3: 606 ms per loop > > The second is a cython version, which makes extension classes to replace the > python ones, and I get a nice speed up of about 30x: > In [2]:%timeit cpy2.runit() > 10 loops, best of 3: 20.9 ms per loop > > The final one is ugly code, making an array of structures instead of lists > of class instances. Where I would have more classes in the second version, > I'd pile those variables all into this one big struct so I can make an array > of them easily. It's ugly, but much faster: > In [3]:%timeit cpy3.runit() > 1000 loops, best of 3: 1.29 ms per loop > > My question is, can I have the best of both worlds (i.e. am I doing > something obviously stupid), or is this the price for cleaner > object-oriented code? Are the the method look-ups killing me in the > object-oriented way? In the larger code I am working on, the difference > between the class versus struct versions is about a factor of 5, not the > factor of 20 above, but there is more going on. Still, a factor of 5 makes > a big difference if the simulation may take a day to run! > thanks, > Brian Blais > > > -- > Brian Blais > [email protected] > http://web.bryant.edu/~bblais > > #================================================ > #==================PYTHON VERSION================ > #================================================ > from numpy import ones,zeros,prod > class Neuron(object): > """docstring for Neuron""" > def __init__(self, N): > super(Neuron, self).__init__() > self.N = N > self.type=1 > self.shape=(N,) > self.spikes=zeros(N,int) > self.old_spikes=zeros(N,int) > self.V=zeros(N) > self.connections_to=[] > > def update(self,t): > > for c in self.connections_to: > V=self.__getattribute__('V') > w=c.weights > V+=w[:,c.incell.spikes.nonzero()[0]].sum(axis=1) > V=self.V > self.V-=self.V/100.0 > self.spikes[:]=1.0 > > > class Connection(object): > """docstring for Connection""" > def __init__(self, incell,outcell): > super(Connection, self).__init__() > self.incell=incell > self.outcell=outcell > self.shape=(outcell.N,incell.N) > self.outcell.connections_to.append(self) > self.weights=ones((outcell.N,incell.N),float) > > > def update(self,t): > pass > > def run_sim( duration,neurons,connections): > > t=0.0 > while t<=duration: > for n in neurons: > n.update(t) > > # for c in connections: > # c.update(t) > t+=1.0 > def runit(): > n1=Neuron(5) > n2=Neuron(3) > > c=Connection(n1,n2) > > run_sim(10000,[n1,n2],[c]) > > > > #================================================ > #==================CYTHON VERSION WITH CLASSES================ > #================================================ > import numpy as np > cimport numpy as np > > DTYPE=np.float64 > ctypedef np.float64_t DTYPE_t > DTYPE_I=np.int > ctypedef np.int_t DTYPE_I_t > from numpy import ones,zeros,prod > cdef class Neuron: > """docstring for Neuron""" > > cdef readonly int N > cdef public np.ndarray spikes > cdef public np.ndarray V > cdef public object connections_to > def __getitem__(self,key): > return self.__getattribute__(key) > > > def __init__(self, N): > self.N = N > self.spikes=zeros(N,int) > self.V=zeros(N) > self.connections_to=[] > > cdef void _update(self,double t): > cdef int __i,__j > > cdef np.ndarray[DTYPE_I_t,ndim=1] spikes > cdef np.ndarray[DTYPE_t,ndim=1] V > cdef np.ndarray[DTYPE_t,ndim=2] _W > cdef int k > > > for c in self.connections_to: > V=self.__getattribute__('V') > _W=c.weights > spikes=c.incell.spikes > k=c.weights.shape[1] > for __i in range(self.N): > for __j in range(k): > if spikes[__j]: > V[__i]+=_W[__i,__j] > > V=self.V > spikes=self.spikes > for __i in range(self.N): > V[__i]-=V[__i]/100.0 > spikes[__i]=1 > > > def update(self,double t): > self._update(t) > > > cdef class Connection: > """docstring for Connection""" > cdef public object incell,outcell > cdef public object shape > cdef public np.ndarray weights > > def __init__(self, incell,outcell): > self.incell=incell > self.outcell=outcell > self.shape=(outcell.N,incell.N) > self.outcell.connections_to.append(self) > self.weights=ones((outcell.N,incell.N),float) > > > def update(self,t): > pass > > def run_sim(duration,neurons,connections): > > t=0.0 > while t<=duration: > for n in neurons: > n.update(t) > > # for c in connections: > # c.update(t) > t+=1.0 > > #================================================ > #==================CYTHON VERSION WITH STRUCTS================ > #================================================ > import numpy as np > cimport numpy as np > > DTYPE=np.float64 > ctypedef np.float64_t DTYPE_t > > cdef double* DoubleData(np.ndarray M): > return <double *>M.data > cdef char* CharData(np.ndarray M): > return <char *>M.data > > cdef int* IntData(np.ndarray M): > return <int *>M.data > cdef struct Neuron_Group_struct: > int N > int *spikes > double *V > int type > int number_of_connections_to > > # hard-coded maximum numbers...yuck! > int *c_spikes[31] > int c_num_incell[31],c_num_outcell[31] > double *c_weights[31] > > > # function to initialize the struct > cdef Neuron_Group_struct init_Neuron_Group(object self): > cdef Neuron_Group_struct s > s.type=self.type > s.N=self.N > s.spikes=IntData(self.spikes) > s.V=DoubleData(self.V) > s.number_of_connections_to=len(self.connections_to) > > cdef int cg > > for cg from 0<=cg<s.number_of_connections_to: > s.c_spikes[cg]=IntData(self.connections_to[cg].incell.spikes) > s.c_num_incell[cg]=self.connections_to[cg].incell.N > s.c_num_outcell[cg]=self.connections_to[cg].outcell.N > > s.c_weights[cg]=DoubleData(self.connections_to[cg].weights) > > cdef void Test_update(Neuron_Group_struct *s,object self,double t): > cdef int __i,__j > cdef int number_of_connections_to > number_of_connections_to=s.number_of_connections_to > cdef int *spikes > cdef int num_incell,num_outcell > cdef int cg,ni,no > cdef double *weights > cdef double *V > > V=s.V > > for cg from 0<=cg<number_of_connections_to: > spikes=s.c_spikes[cg] > num_incell=s.c_num_incell[cg] > num_outcell=s.c_num_outcell[cg] > > weights=s.c_weights[cg] > for ni from 0<=ni<num_incell: > if spikes[ni]: > for no from 0<=no<num_outcell: > V[ni]+=weights[no+ni*num_outcell] > V=s.V > spikes=s.spikes > for no from 0<=no<num_outcell: > V[no]-=V[no]/100.0 > spikes[no]=1 > > cdef nupdate(Neuron_Group_struct *s,object self,double t): > if s.type==-1: > self.update(t) > return > if s.type==0: # Silent Neuron > pass > elif s.type==1: # Constant Fixed > Test_update(s,self,t) > > def run_sim(duration,neurons,connections): > cdef long long t > cdef int i > cdef int ln,lc > ln=len(neurons) > lc=len(connections) > cdef long long num_iter > num_iter=duration > > cdef Neuron_Group_struct Sn[31] > for i from 0<=i<ln: > Sn[i]=init_Neuron_Group(neurons[i]) > for t from 0<=t<num_iter: > for i from 0<=i<ln: > nupdate(&Sn[i],neurons[i],t) > > > > _______________________________________________ > Cython-dev mailing list > [email protected] > http://codespeak.net/mailman/listinfo/cython-dev > > -- Lisandro Dalcín --------------- Centro Internacional de Métodos Computacionales en Ingeniería (CIMEC) Instituto de Desarrollo Tecnológico para la Industria Química (INTEC) Consejo Nacional de Investigaciones Científicas y Técnicas (CONICET) PTLC - Güemes 3450, (3000) Santa Fe, Argentina Tel/Fax: +54-(0)342-451.1594 _______________________________________________ Cython-dev mailing list [email protected] http://codespeak.net/mailman/listinfo/cython-dev
