【发布时间】:2021-03-15 23:00:55
【问题描述】:
我想使用数百万个数据点运行 100k+ 次模拟,这些数据点表示为小数。我选择小数而不是浮点数以获得浮点精度和便于对我的逻辑进行单元测试(因为 0.1 + 0.1 + 0.1 不等于 0.3 和浮点数...)。
我希望通过使用 PyPy 来加速模拟。但是在我的测试过程中,我发现 PyPy 根本无法处理 decimal.Decimal 甚至 _pydecimal.Decimal - 并且比 CPython 解释器(它使用 C 来处理 decimal.Decimal 算术)慢得多。所以我复制/粘贴了我的整个代码库,并用floats 替换了所有Decimals,性能提升巨大:PyPy 比CPython 快x60-x70 倍——牺牲了准确性。
是否有任何解决方案可以在 PyPy 中使用小数精度来提高性能?我“可以”维护两个代码库:float 用于批量运行 100k 模拟,Decimal 用于稍后检查有趣的结果 - 但这承担了维护两个代码库的开销...
以下是我在Raspberry Pi 4 (Ubuntu Server 20.10, 4 x 1.5GHZ ARM Cortex-A72, 8GB RAM) 上运行的一些简单测试以进行复制:
test_decimal.py
import time
from decimal import Decimal
start = time.time()
val = Decimal('1.0')
mul = Decimal('1.000001')
for i in range(10 * 1000 * 1000):
val *= mul
end = time.time()
print(f"decimal.Decimal: {val:.8f} in {round(end-start,4)} sec")
test_pydecimal.py
import time
from _pydecimal import Decimal
start = time.time()
val = Decimal('1.0')
mul = Decimal('1.000001')
for i in range(10 * 1000 * 1000):
val *= mul
end = time.time()
print(f"pydecimal.Decimal: {val:.8f} in {round(end-start,4)} sec")
test_float.py
import time
from decimal import Decimal
start = time.time()
val = float('1.0')
mul = float('1.000001')
for i in range(10 * 1000 * 1000):
val *= mul
end = time.time()
print(f"float: {val:.8f} in {round(end-start,4)} sec")
结果
| Test | Python 3.8.6 (GCC 10.2.0) | Python 3.6.9 -PyPy 7.3.1 with GCC 10.2.0 |
|---|---|---|
| test_decimal | 5.1131 sec | 55.0829 sec |
| test_pydecimal | 315.4012 sec | 40.1771 sec |
| test_float | 2.5607 sec | 0.1273 sec |
编辑#1:
- 更新了示例(使用预先计算的乘法器,在
print之外测量时间)和结果表:PyPy 和 CPython 在 Decimals 上的性能总体比较保持不变。 - 模拟主要包括对具有变化值的时间序列数据的基本数学运算(加、减、乘、除)。
【问题讨论】:
-
我没有对其进行分析,但
print()可能会扭曲您的结果。我敢打赌,将decimal.Decimal转换为str比float需要更多的努力。尝试不使用print()的计时实验。重要的是要了解您也在计时print(),这不是正确的数据操作计时方法,除非您真的想要计时print()。计时print()不可靠的原因之一是由于缓冲。 -
在变量中预先计算
float('1.000001')时,val是相同的,但 Python 的执行速度快 4 倍,PyPy 的执行速度快 63 倍...顺便说一下,精度是多少?你想做什么样的手术? -
@JérômeRichard 感谢您的输入 - 我使用预先计算的
val更新了示例,并更新了结果表并添加了有关计算的信息。8的精度就足够了。
标签: python performance math floating-point pypy