PyLongObject: just use GMP?

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)
1 Like