我试图弄清楚Python / Numpy是否是开发我的数字软件的可行替代方案,该软件已经在C ++中提供。为了在Python / Numpy中获得性能,需要“向量化”代码。但事实证明,一旦我摆脱了非常简单的例子,我就很难对代码进行矢量化(我不是在讨论SIMD指令,而是在没有循环的情况下使用“高效的Numpy代码”)。这是一个我希望在Python / Numpy中有效获得的算法。
这是我想加速的Python算法
import numpy as np
n = 1000000
data = np.arange(1.0, 2.0, 1.0 / n)
def newton(u):
x = 2.0
while True:
f = x**2 - u
df_dx = 2 * x
dx = f / df_dx
if (abs(dx) <= 1.0e-7):
break
x -= dx
return x
result = map(newton, data)
print result[n - 1]
以下是C ++ 11中的算法版本
#include <iostream>
#include <vector>
#include <cmath>
int main (int argc, char const *argv[]) {
auto n = std::size_t{100000000};
auto v = std::vector<double>(n + 1);
for(size_t k = 0; k < v.size(); ++k) {
v[k] = 1.0 + static_cast<double>(k) / n;
}
auto result = std::vector<double>(n + 1);
for(size_t k = 0; k < v.size(); ++k) {
auto x = double{2.0};
while(true) {
auto f = double{x * x - v[k]};
auto df_dx = double{2 * x};
auto dx = double{f / df_dx};
if (std::abs(dx) <= 1.0e-7) {
break;
}
x -= dx;
}
result[k] = x;
}
auto somme = double{0.0};
for(size_t k = 0; k < result.size(); ++k) {
somme += result[k];
}
std::cout << somme << std::endl;
return 0;
}
在我的机器上运行需要2.9秒。有没有办法制作一个快速的Python / Numpy算法来做同样的事情(我愿意得到的东西慢不到5倍)。
感谢。
答案 0 :(得分:2)
您可以有效地使用numpy执行第1步:
1.0 + np.arange(n + 1) / n
但是我认为您需要使用np.vectorize()方法将x反馈到计算值中,并且它不是一个有效的函数(基本上是python循环的包装器)。如果您可以使用scipy,那么内置的方法可能会执行您想要的http://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.optimize.newton.html
编辑:考虑到这一点后,我跟进了@ ev-br的观点并尝试了一些替代方案。掩码使用了太多的处理但是abs()。max()非常快,因此折衷可能是将问题分成块&#34;在数组的第一维和迭代方向。以下在我的低功耗笔记本电脑上做得不是太差(<20s) - 肯定比np.vectorize()或我能找到的任何scipy解决系统快得多。 (如果我将m设置得太大,它就会耗尽某些东西(记忆?)并完全停止!)n = 100000000
m = 5000000
block = 3
u = 1.0 + np.arange(n + 1) / n
x = np.full(u.shape, 2.0)
dx = np.ones(u.shape)
for i in range(0, n, m):
while np.abs(dx[i:i+m]).max() > 1.0e-7:
for j in range(block):
dx[i:i+m] = (x[i:i+m] ** 2 - u[i:i+m]) / (2 * x[i:i+m])
x[i:i+m] -= dx[i:i+m]
答案 1 :(得分:1)
这是一个玩具示例。请注意,通常矢量化意味着编写代码就好像您正在操纵数字,并让numpy发挥其魔力:
>>> import numpy as np
>>> a = np.array([1., 2., 3.])
>>> def f(x):
... return x**2 - a, 2.*x # function and derivative
>>>
>>> def newt(f, x0):
... x = np.asarray(x0)
... for _ in range(5): # hardcode the number of iterations (I know)
... v, dv = f(x)
... x -= v / dv
... return x
>>>
>>> newt(f, [1., 1., 1.])
array([ 1. , 1.41421356, 1.73205081])
如果这是一个性能瓶颈,那么这不太可能与手写的C ++代码竞争:首先,你要用所有开销来操作python对象;然后numpy可能会在引擎盖下做一堆数组分配。
一个经常可行的策略是首先在python / numpy中编写内容,然后将瓶颈转移到已编译的代码中 - 例如由Cython包装的Cython或C ++。在这种特殊情况下,因为你已经有了C ++代码,所以用Cython包装它可能是最容易的,但是YMMV。
答案 2 :(得分:0)
我不打算将小代码片段作为解决方案,但这里有一些东西可以让你开始。我有一种强烈的怀疑,你只是在python中声明这样一个数组而没有花费太多时间,所以我会帮助你。
就平方根来说,请添加你的示例python代码,我会看到从那时起我可以帮助优化的内容。在我的例子中,使用默认的numpy函数/方法找到了根和和。
def summing():
n = 1000000
ar = np.arange(0, n)
ar = ar/float(n)
ar = ar + np.ones(n)
sqrt = np.sqrt(ar)
return np.sum(ar)
简而言之,要获得起始阵列,最好使用“解决方法”。
ar
ar
除以n
。这会让我们成为1/n, 2/n ...
成员1.0
这让我们得到了我们追求的完整数组[ 1., 1.000001, 1.000002, ..., 1.999998, 1.999999])
。如果我理解你的话。平均10个连续执行时间为0.018786
秒。