he decidido crear algunos puntos de referencia del problema original porque yo también necesitaba para calcular de forma recursiva algo que no podía ser vectorizado. Utilicé abs() para la base del exponente porque el resultado no está definido cuando la base es negativa. Ejemplo de cálculo a través de un bucle normal de la gama Numpy:
alen=100000
Tm = np.random.normal(size=alen).astype('float64')
tau = np.random.normal(size=alen).astype('float64')
def rec_numpy_loop(Tm,tau,alen):
T=np.empty(alen)
T[0]=0.0
for i in range(1,alen):
T[i] = Tm[i] + (abs(T[i-1] - Tm[i]))**(-tau[i])
return T
Los resultados son que la compilación de una función como un módulo Cython es muy rápido tanto en ejecución (14 veces más rápido que cualquier implementación de Python) y escribir el código.
Otros datos de interés: Varias implementaciones de Numpy varían ligeramente con la implementación más rápida utilizando la notación a.item() y a.itemset(). Extraño, pero agregar listas funciona a la par con las matrices numeradas preasignadas. Jugar con memviews en Cython no dio ningún impulso significativo al rendimiento del código. El código de Fortran era muy corto pero casi imposible de depurar y, al final, Cython apareció más rápido que Fortran con f2py, aunque marginalmente. La extensión C tomó mucho tiempo para escribir porque tiene demasiadas repeticiones y al final funciona con la misma velocidad que Cython. Pure C con matrices C puras fue dos veces más rápido que cualquier otro llamado desde Python.
No soy un profesional de C/C++ así que tal vez uno puede escribir un programa más rápido. código
Looping over Numpy array:
In [57]: %timeit -o rec_numpy_loop(Tm,tau,alen)
10 loops, best of 3: 87.9 ms per loop
Using a.item() and a.itemset() with Numpy:
In [58]: %timeit -o rec_numpy_loop_item(Tm,tau,alen)
10 loops, best of 3: 74.3 ms per loop
Using np.fromiter() from Numpy:
In [59]: %timeit -o rec_numpy_iter(Tm,tau,alen)
10 loops, best of 3: 78.1 ms per loop
Using Numpy arrays with data but appending to a List:
In [60]: %timeit -o rec_py_loop(Tm,tau,alen)
10 loops, best of 3: 91 ms per loop
Calling a function compiled to Cython:
In [61]: %timeit -o rec_cy_loop(Tm,tau,alen)
100 loops, best of 3: 6.46 ms per loop
Using Memviews in Cython:
In [62]: %timeit -o rec_cy_loop_memviews(Tm,tau,alen)
100 loops, best of 3: 6.26 ms per loop
Using Memviews both for looping and as input variables in Cython:
In [63]: %timeit -o rec_cy_loop_memviews_w_input(Tm,tau,alen)
100 loops, best of 3: 6.53 ms per loop
Calling a fortran function compiled using f2py:
In [64]: %timeit -o rec_fortran(Tm,tau,alen)
100 loops, best of 3: 6.78 ms per loop
Calling a function compiled as C extension of Python using Numpy arrays:
In [65]: %timeit -o rec_c(Tm,tau,alen)
100 loops, best of 3: 6.22 ms per loop
Doing everything in C using dynamic C arrays:
1000 loops,best of 3: 2.751 ms per loop
Python:
import timeit
import numpy as np
from rec_cy_loop import rec_cy_loop
#python setup_rec_cy_loop.py build_ext --inplace
from rec_cy_loop_memviews import rec_cy_loop_memviews
from rec_cy_loop_memviews_w_input import rec_cy_loop_memviews_w_input
from rec_c import rec_c
#python setup.py build_ext --inplace
from rec_fortran import rec_fortran
#f2py -c rec.f95 -m rec_fortran
alen=100000
Tm = np.random.normal(size=alen).astype('float64')
tau = np.random.normal(size=alen).astype('float64')
def rec_numpy_loop(Tm,tau,alen):
T=np.empty(alen)
T[0]=0.0
for i in range(1,alen):
T[i] = Tm[i] + (abs(T[i-1] - Tm[i]))**(-tau[i])
return T
def rec_numpy_loop_item(Tm,tau,alen):
T=np.empty(alen)
Ti=T.item
Tis=T.itemset
Tmi=Tm.item
taui=tau.item
Tis(0,0.0)
for i in range(1,alen):
Tis(i,Tmi(i) + (abs(Ti(i-1) - Tmi(i)))**(-taui(i)))
return T
def it(Tm,tau):
T=0.0
i=0
while True:
yield T
i+=1
T=Tm[i] + (abs(T - Tm[i]))**(-tau[i])
def rec_numpy_iter(Tm,tau,alen):
return np.fromiter(it(Tm,tau), np.float64, alen)
def rec_py_loop(Tm,tau,alen):
T = [0.0]
for i in range(1,alen):
T.append(Tm[i] + (abs(T[i-1] - Tm[i]))**(-tau[i]))
return np.array(T)
T=rec_numpy_loop(Tm,tau,alen)
Titer=rec_numpy_loop_item(Tm,tau,alen)
np.sum(np.abs(Titer-T))
Titer=rec_numpy_iter(Tm,tau,alen)
np.sum(np.abs(Titer-T))
Titer=rec_py_loop(Tm,tau,alen)
np.sum(np.abs(Titer-T))
Titer=rec_cy_loop(Tm,tau,alen)
np.sum(np.abs(Titer-T))
Titer=rec_cy_loop_memviews(Tm,tau,alen)
np.sum(np.abs(Titer-T))
Titer=rec_cy_loop_memviews_w_input(Tm,tau,alen)
np.sum(np.abs(Titer-T))
Titer=rec_fortran(Tm,tau,alen)
np.sum(np.abs(Titer-T))
Titer=rec_c(Tm,tau,alen)
np.sum(np.abs(Titer-T))
%timeit -o rec_numpy_loop(Tm,tau,alen)
%timeit -o rec_numpy_loop_item(Tm,tau,alen)
%timeit -o rec_numpy_iter(Tm,tau,alen)
%timeit -o rec_py_loop(Tm,tau,alen)
%timeit -o rec_cy_loop(Tm,tau,alen)
%timeit -o rec_cy_loop_memviews(Tm,tau,alen)
%timeit -o rec_cy_loop_memviews_w_input(Tm,tau,alen)
%timeit -o rec_fortran(Tm,tau,alen)
%timeit -o rec_c(Tm,tau,alen)
Cython rec_cy_loop:
rec_cy_loop_memviews
cdef extern from "math.h":
double exp(double m)
import cython
import numpy as np
cimport numpy as np
from numpy cimport ndarray
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.infer_types(True)
def rec_cy_loop(ndarray[np.float64_t, ndim=1] Tm,ndarray[np.float64_t, ndim=1] tau,int alen):
cdef ndarray[np.float64_t, ndim=1] T=np.empty(alen)
cdef int i
T[0]=0.0
for i in range(1,alen):
T[i] = Tm[i] + (abs(T[i-1] - Tm[i]))**(-tau[i])
return T
Cython:
cdef extern from "math.h":
double exp(double m)
import cython
import numpy as np
cimport numpy as np
from numpy cimport ndarray
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.infer_types(True)
def rec_cy_loop_memviews(ndarray[np.float64_t, ndim=1] Tm,ndarray[np.float64_t, ndim=1] tau,int alen):
cdef ndarray[np.float64_t, ndim=1] T=np.empty(alen)
cdef int i
cdef double[::1] T2 = T
cdef double[::1] Tm2 = Tm
cdef double[::1] tau2 = tau
T2[0]=0.0
for i in range(1,alen):
T2[i] = Tm2[i] + (abs(T2[i-1] - Tm2[i]))**(-tau2[i])
return T2
Cython rec_cy_loop_memviews_w_input:
cdef extern from "math.h":
double exp(double m)
import cython
import numpy as np
cimport numpy as np
from numpy cimport ndarray
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.infer_types(True)
def rec_cy_loop_memviews_w_input(double[::1] Tm,double[::1] tau,int alen):
cdef double[::1] T=np.empty(alen)
cdef int i
T[0]=0.0
for i in range(1,alen):
T[i] = Tm[i] + (abs(T[i-1] - Tm[i]))**(-tau[i])
return T
función Fortran compilado a través de f2py:
subroutine rec_fortran(tm,tau,alen,result)
integer*8, intent(in) :: alen
real*8, dimension(alen), intent(in) :: tm
real*8, dimension(alen), intent(in) :: tau
real*8, dimension(alen) :: res
real*8, dimension(alen), intent(out) :: result
res(1)=0
do i=2,alen
res(i) = tm(i) + (abs(res(i-1) - tm(i)))**(-tau(i))
end do
result=res
end subroutine rec_fortran
Pure C:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <windows.h>
#include <sys\timeb.h>
double randn() {
double u = rand();
if (u > 0.5) {
return sqrt(-1.57079632679*log(1.0 - pow(2.0 * u - 1, 2)));
}
else {
return -sqrt(-1.57079632679*log(1.0 - pow(1 - 2.0 * u,2)));
}
}
void rec_pure_c(double *Tm, double *tau, int alen, double *T)
{
for (int i = 1; i < alen; i++)
{
T[i] = Tm[i] + pow(fabs(T[i - 1] - Tm[i]), (-tau[i]));
}
}
int main() {
int N = 100000;
double *Tm= calloc(N, sizeof *Tm);
double *tau = calloc(N, sizeof *tau);
double *T = calloc(N, sizeof *T);
double time = 0;
double sumtime = 0;
for (int i = 0; i < N; i++)
{
Tm[i] = randn();
tau[i] = randn();
}
LARGE_INTEGER StartingTime, EndingTime, ElapsedMicroseconds;
LARGE_INTEGER Frequency;
for (int j = 0; j < 1000; j++)
{
for (int i = 0; i < 3; i++)
{
QueryPerformanceFrequency(&Frequency);
QueryPerformanceCounter(&StartingTime);
rec_pure_c(Tm, tau, N, T);
QueryPerformanceCounter(&EndingTime);
ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart;
ElapsedMicroseconds.QuadPart *= 1000000;
ElapsedMicroseconds.QuadPart /= Frequency.QuadPart;
if (i == 0)
time = (double)ElapsedMicroseconds.QuadPart/1000;
else {
if (time > (double)ElapsedMicroseconds.QuadPart/1000)
time = (double)ElapsedMicroseconds.QuadPart/1000;
}
}
sumtime += time;
}
printf("1000 loops,best of 3: %.3f ms per loop\n",sumtime/1000);
free(Tm);
free(tau);
free(T);
}
suena como un gran candidato para una lista de comprensión, pero no puedo intentar escribir una ahora. Me interesaría ver lo que otros piensan. – duffymo
Si 'tau' es un vector, ¿debería ser indexado por' i' también? – mtrw
¿Cuál es la condición de frontera? I.E., ¿qué sucede cuando 'i = 0'? –