2024-10-17 06:15:15 发布
网友
在Python-showhere中有两种实现经典Runge-Kutta格式的方法。第一个使用lambda函数,第二个没有lambda函数。在
哪一个会更快?为什么?
@berna111和@matt2000都是正确的。lambda版本由于函数调用而产生额外的开销。尾部调用优化将尾部调用转换为while循环(即自动将lambda版本转换为while版本),消除了函数调用开销。在
请参阅https://stackoverflow.com/a/13592002/7421639,了解Python为什么不自动进行这种优化,而您必须使用像cocon这样的工具来进行预处理过程传递。在
我调整了给定链接中的代码,并使用cProfile来比较这两种技术:
import numpy as np import cProfile as cP def theory(t): return (t**2 + 4.)**2 / 16. def f(x, y): return x * np.sqrt(y) def RK4(f): return lambda t, y, dt: ( lambda dy1: ( lambda dy2: ( lambda dy3: ( lambda dy4: (dy1 + 2*dy2 + 2*dy3 + dy4)/6 )( dt * f( t + dt , y + dy3 ) ) )( dt * f( t + dt/2, y + dy2/2 ) ) )( dt * f( t + dt/2, y + dy1/2 ) ) )( dt * f( t , y ) ) def test_RK4(dy=f, x0=0., y0=1., x1=10, n=10): vx = np.empty(n+1) vy = np.empty(n+1) dy = RK4(f=dy) dx = (x1 - x0) / float(n) vx[0] = x = x0 vy[0] = y = y0 i = 1 while i <= n: vx[i], vy[i] = x + dx, y + dy(x, y, dx) x, y = vx[i], vy[i] i += 1 return vx, vy def rk4_step(dy, x, y, dx): k1 = dx * dy(x, y) k2 = dx * dy(x + 0.5 * dx, y + 0.5 * k1) k3 = dx * dy(x + 0.5 * dx, y + 0.5 * k2) k4 = dx * dy(x + dx, y + k3) return x + dx, y + (k1 + k2 + k2 + k3 + k3 + k4) / 6. def test_rk4(dy=f, x0=0., y0=1., x1=10, n=10): vx = np.empty(n+1) vy = np.empty(n+1) dx = (x1 - x0) / float(n) vx[0] = x = x0 vy[0] = y = y0 i = 1 while i <= n: vx[i], vy[i] = rk4_step(dy=dy, x=x, y=y, dx=dx) x, y = vx[i], vy[i] i += 1 return vx, vy cP.run("test_RK4(n=10000)") cP.run("test_rk4(n=10000)")
得到了:
所以我想说,“lambda”实现中的function calloverhead使它变慢了。在
lambda
不过,请注意,我似乎在某种程度上失去了一些精确性,因为尽管结果彼此一致,但结果比示例中的结果更差:
>>> vx, vy = test_rk4() >>> vy array([ 1. , 1.56110667, 3.99324757, ..., 288.78174798, 451.27952013, 675.64427775]) >>> vx, vy = test_RK4() >>> vy array([ 1. , 1.56110667, 3.99324757, ..., 288.78174798, 451.27952013, 675.64427775])
如果使用实现尾部调用优化的Coconuttranspiler对代码进行预处理,则它们完全等效(与未处理的更快版本一样快),因此可以使用更方便的样式。在
# Save berna1111's code as rk4.coco; no modifications necessary. $ coconut target 3 rk4.coco & python3 rk4.py 50007 function calls in 0.055 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.097 0.097 <string>:1(<module>) 40000 0.038 0.000 0.038 0.000 rk4.py:243(f) 1 0.000 0.000 0.000 0.000 rk4.py:246(RK4) 10000 0.007 0.000 0.088 0.000 rk4.py:247(<lambda>) 1 0.010 0.010 0.097 0.097 rk4.py:250(test_RK4) 1 0.000 0.000 0.097 0.097 {built-in method builtins.exec} 2 0.000 0.000 0.000 0.000 {built-in method numpy.core.multiarray.empty} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 50006 function calls in 0.057 seconds Ordered by: standard name ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 0.057 0.057 <string>:1(<module>) 40000 0.030 0.000 0.030 0.000 rk4.py:243(f) 10000 0.019 0.000 0.049 0.000 rk4.py:265(rk4_step) 1 0.007 0.007 0.057 0.057 rk4.py:273(test_rk4) 1 0.000 0.000 0.057 0.057 {built-in method builtins.exec} 2 0.000 0.000 0.000 0.000 {built-in method numpy.core.multiarray.empty} 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
@berna111和@matt2000都是正确的。lambda版本由于函数调用而产生额外的开销。尾部调用优化将尾部调用转换为while循环(即自动将lambda版本转换为while版本),消除了函数调用开销。在
请参阅https://stackoverflow.com/a/13592002/7421639,了解Python为什么不自动进行这种优化,而您必须使用像cocon这样的工具来进行预处理过程传递。在
我调整了给定链接中的代码,并使用cProfile来比较这两种技术:
得到了:
^{pr2}$所以我想说,“
lambda
”实现中的function calloverhead使它变慢了。在不过,请注意,我似乎在某种程度上失去了一些精确性,因为尽管结果彼此一致,但结果比示例中的结果更差:
如果使用实现尾部调用优化的Coconuttranspiler对代码进行预处理,则它们完全等效(与未处理的更快版本一样快),因此可以使用更方便的样式。在
相关问题 更多 >
编程相关推荐