为什么在 python 中安装 sagemath 会提高 mpmath 的性能?

问题描述

我注意到 mpmath 的性能,尽管听起来很奇怪,取决于是否安装了 sagemath,无论 sage 模块是否在当前会话中加载。特别是,我在使用多个精度浮点数的操作时遇到了这种情况。

示例:

from mpmath import mp
import time
mp.prec = 650

t = time.time()
for i in range(1000000):
    x_mpmath + y_mpmath
w = time.time()
print('plus:\t',(w-t),'μs')
t = time.time()
for i in range(1000000):
    x_mpmath * y_mpmath
w = time.time()
print('times:\t','μs')

# If sagemath is installed:
# plus:  0.12919950485229492 μs
# times: 0.17601895332336426 μs
#
# If sagemath is *not* installed:
# plus:  0.6239776611328125 μs
# times: 0.6283771991729736 μs

虽然在这两种情况下,模块 mpmath 是完全相同的

import mpmath
print(mpmath.__file__)
# /usr/lib/python3.9/site-packages/mpmath/__init__.py

我认为 mpmath 的后端将取决于一些 sagemath 依赖项,如果缺少它,它会回退到优化程度较低的后端,但我无法弄清楚它到底是什么。我的目标是能够只安装所需的包来加速 mpmath,而不是安装所有的 sagemath。

因为这很可能取决于事物的打包方式,所以您可能需要了解我的系统的详细信息:我使用的是 Arch Linux,并且所有软件包都更新到了最新版本(sagemath 9.3、mpmath 1.2.1、蟒蛇 3.9.5).

解决方法

我找到了解释。在 /usr/lib/python3.9/site-packages/mpmath/libmp/backend.py 的第 82 行有

if 'MPMATH_NOSAGE' not in os.environ:
    try:
        import sage.all
        import sage.libs.mpmath.utils as _sage_utils
        sage = sage.all
        sage_utils = _sage_utils
        BACKEND = 'sage'
        MPZ = sage.Integer
    except:
        pass

如果安装了 sagemath,这将加载所有 sage,并将其设置为后端。这意味着接下来会加载以下库:

        import sage.libs.mpmath.ext_libmp as ext_lib

从第 1407 行的 /usr/lib/python3.9/site-packages/mpmath/libmp/libmpf.py 开始。通过查看该模块的 __file__,可以看到它是一个 .so 对象,因此被编译,因此速度更快。

这也意味着通过将 MPMATH_NOSAGE 导出到任何非空值将强制后端为默认值(python 或 gmpy),并且确实我可以确认我在问题中编写的代码在此中运行速度较慢情况下,即使安装了 sagemath。