使用 PyPy 或 Cython 加速循环?



我有一个 Python 脚本,其中包含一个循环,其中包含大量调用scipy.optimize.fsolve(每个时间步长 99 (55 + 54( 次,现在我需要大约 10^5 个时间步长(。脚本的其余部分也不是很快,但据我从Spyder Profiler的输出中可以看出,对fsolve的调用是迄今为止最耗时的。使用我当前的设置,运行脚本需要 10 多个小时,因此我可以稍微加快速度。

根据我在这里和互联网上其他地方读到的内容,我已经第一次尝试了 PyPy:将其安装在 conda 下的单独环境中(MacOS 10.15.5、pypy 7.3.1 和 pypy3.6 7.3.1(,以及它自己的 numpy、scipy 和 pandas 版本,但到目前为止,它实际上比 Python 慢一点(195 秒与 171 秒, 对于 100 个时间步长(。

从我在这里读到的内容(PyPy 状态博客,17 年 10 月(来看,这可能与使用 numpy 而不是 numpypy 和/或重复分配临时数组有关。除了调用 fsolve 1000 万+ 次之外,我还使用了相当多的 numpy 数组,所以据我所知这是有道理的。

问题是,我不是开发人员,而且我对 PyPy 完全陌生,所以像 JIT 跟踪这样的术语对我来说意义不大,破译其中的内容对我来说可能具有挑战性。此外,2017年10月曾经是这样的事情现在可能不再适用。另外,即使我设法加快了 numpy 数组位的速度,我仍然不确定解问题部分。

谁能指出我是否值得在 PyPy 上投入时间?或者在这种情况下,Cython会更合适吗?甚至是 mpi4py?

如果有帮助,我很乐意分享我的代码,但它包含一个包含 800 多行代码的模块,所以仅仅将其包含在这篇文章中对我来说似乎不是一个好主意。

非常感谢! 西塔

编辑:感谢大家的快速和善意的回应!这是一个公平的观点,关于需要查看我的代码,我把它放在这里(链接有效期至 2020 年 6 月 19 日(。Arterial_1D.py是模块,CoronaryTree.py 是调用Arterial_1D.py的脚本。对于一个最小的工作示例,我添加了一行额外的行,在这种情况下要取消注释(在代码中清楚地标记(。此外,我将时间步长数设置为 100,以使代码在合理的时间内运行(在我的例子中,最小示例为 0.61 秒,完整冠状树为 37.3 秒(。

编辑2:我愚蠢,在我的原始帖子中,我提到了分别使用PyPy和Python运行100个代码步骤的197和171秒,但在这种情况下,我从PyPy环境中调用Python,所以它使用的是PyPy版本的Numpy。在我的基本环境中,运行 100 步需要 30 多秒。所以在这种情况下,PyPy 比 Python 慢很多,这促使我无论如何都要研究这篇 PyPy 状态博客文章。

如果不查看您的代码,我们就无法真正帮助您进行优化。但是,既然你在那里有相当多的描述,让我回答我认为你可以尝试加快速度的内容。

第一件事是第一位的。西皮图书馆。从scipy.optimize.fsolve的源代码来看,它围绕着MINPACK的hybrd和hybrj算法,它们是相当快的FORTRAN子例程。因此,在您的情况下,切换到 PyPy 对于这个已确定的瓶颈没有多大好处,如果有的话。

你能做些什么来优化你的西比解?最明显的数值操作之一是矢量化函数的参数。但似乎您正在运行一种时间步长算法,并且大多数标准时间步长算法无法在时间上矢量化。 如果您的"每个时间步长 XX 次"是一种隐式的空间循环(即您的网格(,您可以考虑对其进行矢量化以实现一些速度提升。接下来是放大函数的猜测/起始根估计值。看看你是否可以修改你的算法,以在整个时间间隔内利用一个好的起始解决方案(做一些文献挖掘(。请注意,这与"编程"的关系不大,而是您对数值方法的了解。

接下来,关于您对"脚本的其余部分也不是很快"的评论。好吧,我会和 Cython 一起去处理代码中剩余的 python 部分,尤其是循环。它发展非常活跃,社区很棒,并且经过了实战考验。我个人在许多 HPC 类型的问题中使用它。Cython还有一个方便的html注释,突出了原生python实现可能进行的潜在优化。

希望这有帮助!干杯

最新更新