I would like to run some 100k+ simulations with some millions of data points, which are represented as decimals. I choose decimals over floats for floating point accuracy and ease of unit testing my logic (since 0.1 + 0.1 + 0.1 does not equal 0.3 with floats...).
My hope was to speed up the simulations by using PyPy. But during my testing I encountered that PyPy does not handle decimal.Decimal or even _pydecimal.Decimal well at all - and gets dramatically slower than the CPython interpreter (which uses C for decimal.Decimal arithmetics). So I copy/pasted my whole codebase and replaced all Decimals with floats and the performance increase was huge: x60-x70 times faster with PyPy than CPython - with the sacrifice of accuracy.
Are there any solutions to use Decimals precision in PyPy with the performance benefit? I "could" maintain two codebases: float for batch running the 100k simulations, Decimal for inspecting the interesting results later - but this bears the overhead of maintaining two codebases...
Here are some simple tests I ran on a Raspberry Pi 4 (Ubuntu Server 20.10, 4 x 1.5GHZ ARM Cortex-A72, 8GB RAM) for reproduction:
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")
Results
| 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 |
Edit #1:
- Updated the examples (use precomputed multiplicator, measure time outside of
print) and the results table: The overall comparison between PyPy and CPython performance on Decimals stays the same. - The simulation mostly consists of the basic math operations (add, subtract, multiply, divide) over time-series data with changing values.