Not with 1000x speedup case wrt gmpy2, of course. Major problems here are from the cpyext layer. Matti Picus suggested me to try HPy or CFFI. But this in turn make things noticeable slower on CPython as e.g. HPy uses heap types.
But the point is that both PyPy and CPython come with some optimizations for builtin types, that are impossible for extension types. And more is coming, I think.
Here few benchmarks, that use explicit loops. With CPython v3.15.0a7 and PGO or PGO+LTO+JIT.
Harmonic numbers:
| Benchmark | ref-jit | ref | gmp |
|---|---|---|---|
| H(100) | 403 us | 476 us: 1.18x slower | 757 us: 1.88x slower |
| H(1000) | 5.66 ms | 6.54 ms: 1.16x slower | 10.2 ms: 1.80x slower |
| Geometric mean | (ref) | 1.17x slower | 1.84x slower |
Sum (note, that jit-ted interpreter is slower here):
| Benchmark | ref | ref-jit | gmp |
|---|---|---|---|
| sum(100) | 5.94 us | 6.78 us: 1.14x slower | 16.3 us: 2.74x slower |
| sum(1000) | 63.8 us | 75.6 us: 1.19x slower | 159 us: 2.50x slower |
| Geometric mean | (ref) | 1.16x slower | 2.62x slower |
Collatz benchmark (bench/collatz.py in the python-gmp):
| Benchmark | ref-jit | ref | gmp |
|---|---|---|---|
| collatz0(97) | 22.2 us | 24.8 us: 1.12x slower | 85.9 us: 3.86x slower |
| collatz0(871) | 34.8 us | 38.7 us: 1.11x slower | 130 us: 3.74x slower |
| collatz0((1<<128)+31) | 339 us | 351 us: 1.03x slower | 667 us: 1.97x slower |
| collatz1(97) | 22.0 us | 23.1 us: 1.05x slower | 101 us: 4.59x slower |
| collatz1(871) | 33.8 us | 35.4 us: 1.05x slower | 151 us: 4.48x slower |
| collatz1((1<<128)+31) | 337 us | 325 us: 1.03x faster | 786 us: 2.33x slower |
| collatz2(97) | 22.0 us | 24.5 us: 1.11x slower | 85.9 us: 3.90x slower |
| collatz2(871) | 34.0 us | 36.9 us: 1.08x slower | 129 us: 3.79x slower |
| collatz2((1<<128)+31) | 339 us | not significant | 666 us: 1.96x slower |
| Geometric mean | (ref) | 1.06x slower | 3.24x slower |
# a.py
from fractions import Fraction
import pyperf
#mpz = int
from gmp import mpz
runner = pyperf.Runner()
def mysum(xs):
total = Fraction(mpz(0))
for t in xs:
total += t
return t
for n in [100, 1000]:
xs = [Fraction(mpz(1), mpz(i)) for i in range(1, n + 1)]
runner.bench_func(f"H({n})", mysum, xs)
# b.py
import pyperf
#mpz = int
from gmp import mpz
runner = pyperf.Runner()
def mysum(xs):
total = mpz(0)
for t in xs:
total += t
return t
for n in [100, 1000]:
xs = [mpz(i) for i in range(1, n + 1)]
runner.bench_func(f"sum({n})", mysum, xs)