使用 PyPy 或 Cython 使用 fsolve 加速循环?

Speeding up loop with fsolve using PyPy or 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 个时间步长)。

根据我阅读的内容 here(PyPy 状态博客,17 年 10 月),这可能与使用 numpy 而不是 numpypy,and/or 临时数组的重复分配有关。除了调用 fsolve 超过 1000 万次之外,我还使用了相当多的 numpy 数组,所以据我所知这是有道理的。

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

谁能告诉我花时间在 PyPy 上是否值得?或者 Cython 更适合这种情况?或者甚至是 mpi4py?

如果有帮助,我很乐意分享我的代码,但它包含一个超过 800 行代码的模块,因此仅将其包含在此 post 中对我来说似乎不是一个好主意。

非常感谢! 西塔

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

编辑 2: 我真傻,在我原来的 post 我提到了 197 和 171 秒的时间 运行 我的代码的 100 步分别使用 PyPy 和 Python,但在那种情况下,我从 PyPy 环境中调用了 Python,因此它使用的是 Numpy 的 PyPy 版本。在我的基础环境中,运行宁 100 步需要 30 多秒。所以 PyPy 在这种情况下比 Python 慢很多,这促使我无论如何都要查看这个 PyPy 状态博客 post。

如果不查看您的代码,我们无法真正帮助您进行优化。但是,由于您在上面进行了相当多的描述,所以让我回复一下我认为您可以尝试加快速度的内容。

要事第一。 Scipy 图书馆。 从 scipy.optimize.fsolve 的源代码来看,它包含了 MINPACK 的 hybrd 和 hybrj 算法,这些算法是相当快的 FORTRAN 子例程。所以在你的情况下,切换到 PyPy 不会对这个已确定的瓶颈有任何好处,如果有的话。

您可以做什么来优化您的 Scipy fsolve? 最明显的数值操作之一是向量化函数的参数。但似乎你是 运行 一种时间步长算法,大多数标准时间步长算法无法及时矢量化。如果您的 'XX times per time step' 是每个时间步长(即您的网格)的一种隐式空间循环,您可以考虑对其进行矢量化以提高速度。接下来是放大函数的猜测/起始根估计。看看你是否可以 mod 你的算法在整个时间间隔内利用一个好的起始解决方案(做一些文献挖掘)。请注意,这与 'programming' 关系不大,而与您对数值方法的了解有关。

接下来,关于你对 "rest of the script isn't very fast either" 的评论。 好吧,我会和 Cython 一起去处理你代码的剩余 python 部分,尤其是循环。它的开发非常积极,社区很棒,并且经过了实战测试。我个人已经在许多 HPC 类型的问题中使用过它。 Cython 也有一个方便的 html 注释,突出显示可能超过您的本机 python 实现的潜在优化。

希望对您有所帮助!干杯