我正在用python做很多模拟,模拟系统响应。
我目前一直在使用Runge-Kutta方案,但偶然发现了我正在测试的另一种方案。
在Matlab中进行测试时,与我的Runge-Kutta相比,我获得了出色的性能。但是,当我将其转移到Python时,它的速度明显慢了。
我不确定这是不是真的,或者我是否可以改善编码方式,所以如果可能的话,我想听听您的一些意见。
Matlab中的代码示例:
dt = 0.0001;
f = randn(1, (60 / dt));
ns = length(f);
yo = zeros(3,1);
P1 = [0; 0.001; 0];
F = [1 0.0001 0; 0.001 1 0.0001; 0.001 0 1];
y1 = zeros(3, ns);
tic
for i = 1:ns
y1(:, i) = P1*f(:, i) + F*yo;
yo = y1(:, i);
end
toc
其中循环在0.55-0.61秒内执行。
Python中的代码,例如:
dt = 0.0001
f = np.random.randn(1, int(60 / dt))
ns = np.size(f)
yo = np.zeros((3))
F = np.array([[1, 0.0001, 0], [0.001, 1, 0.0001], [0.001, 0, 1]])
P1 = np.transpose(np.array([[0, 0.0001, 0]]))
y1 = np.zeros((3, ns), order='F')
start_time = time.time()
for i in range(ns-1):
y1[:, i] = np.dot(P1, f[:, i]) + np.reshape(np.dot(F, yo), (3))
yo = y1[: , i]
print("--- %s seconds ---" % (time.time() - start_time))
其中循环在2.8 -3.1秒内执行。
我可以做些改进吗?
感谢您考虑我的问题。
答案 0 :(得分:2)
我建议在评论中使用numba
。这是一个示例:
import numba
import numpy as np
def py_func(dt, F, P1):
f = np.random.randn(1, int(60 / dt))
ns = f.size
yo = np.zeros((3))
y1 = np.zeros((3, ns), order='F')
for i in range(ns-1):
y1[:, i] = np.dot(P1, f[:, i]) + np.reshape(np.dot(F, yo), (3))
yo = y1[: , i]
return yo
@numba.jit(nopython=True)
def numba_func(dt, F, P1):
f = np.random.randn(1, int(60 / dt))
ns = f.size
yo = np.zeros((3))
y1 = np.zeros((3, ns))
for i in range(ns-1):
y1[:, i] = np.dot(P1, f[:, i]) + np.reshape(np.dot(F, yo), (3))
yo = y1[: , i]
return yo
numba
不能使用'F'顺序,因为它使用C型数组,而不是FORTRAN数组。
时间差异如下所示:
纯python循环:
%%timeit
py_func(dt, F, P1)
结果:
2.88 s ± 100 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Numba:
%%timeit
numba_func(dt, F, P1)
结果:
588 ms ± 10.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
答案 1 :(得分:1)
我对您的代码进行了一些优化,对我来说,执行时间从2.8s减少到1.2s左右。在寻找更快的解释器之前,我建议您进行性能分析(请参阅line_profiler),并尝试从最内层循环中删除所有可能的内容。更好的方法是,尝试避免任何显式的“ for”循环,并依赖于numpy函数,例如点,einsum等。
我想仍然有一些优化的地方。我认为我没有改变您的价值观,但请您检查一下。借助numba或cython(cython.org)或pypy(pypy.org)等其他工具,我认为您的执行时间将大大提高。
#!/usr/bin/env python3
import numpy as np
import time
np.random.seed(0)
#@profile
def run():
dt = 0.0001
f = np.random.randn(1, int(60 / dt))
ns = np.size(f)
yo = np.zeros((3))
F = np.array([[1, 0.0001, 0], [0.001, 1, 0.0001], [0.001, 0, 1]])
P1 = np.transpose(np.array([[0, 0.0001, 0]]))
start_time = time.time()
y1 = np.outer(f, P1)
for i in range(ns-1):
y1[i] += F@yo
yo = y1[i]
print("--- %s seconds ---" % (time.time() - start_time))
y1 = y1.T
print(yo)
run()