使用 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 实现的潜在优化。
希望对您有所帮助!干杯
我有一个 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 实现的潜在优化。
希望对您有所帮助!干杯