Skip to content

Use PEP 757 API for fmpz <-> int conversions - #358

Closed
skirpichev wants to merge 1 commit into
flintlib:mainfrom
skirpichev:pep757-conversion/159
Closed

Use PEP 757 API for fmpz <-> int conversions#358
skirpichev wants to merge 1 commit into
flintlib:mainfrom
skirpichev:pep757-conversion/159

Conversation

@skirpichev

Copy link
Copy Markdown

Closes#159

This lacks implementation for Python < 3.14. Not sure how to do this better, conditional compilation seems to be deprecated by Cython.

To my surprise, #324 approach seems to be better. Here my benchmarks.

Export

Benchmarkrefpr324patch
1<<7166 ns169 ns: 1.02x slower169 ns: 1.02x slower
1<<38216 nsnot significant219 ns: 1.01x slower
1<<3003.06 us923 ns: 3.31x faster787 ns: 3.88x faster
1<<300015.9 us2.17 us: 7.34x faster3.70 us: 4.29x faster
1<<1000049.4 us5.93 us: 8.33x faster11.6 us: 4.27x faster
Geometric mean(ref)2.88x faster2.33x faster

Import

Benchmarkrefpr324patch
1<<7454 ns451 ns: 1.01x faster459 ns: 1.01x slower
1<<38471 ns466 ns: 1.01x faster475 ns: 1.01x slower
1<<3004.80 us1.91 us: 2.52x faster1.10 us: 4.37x faster
1<<300023.6 us3.07 us: 7.68x faster4.17 us: 5.65x faster
1<<1000073.0 us6.78 us: 10.76x faster12.5 us: 5.86x faster
Geometric mean(ref)2.92x faster2.69x faster
Details
# bench-export.pyimportosimportpyperf_T=os.getenv('_T')
if_T=="gmpy2.mpz":
fromgmpy2importmpzelif_T=="gmp.mpz":
fromgmpimportmpzelse:
fromflintimportfmpzasmpzcases= ['1<<7', '1<<38', '1<<300', '1<<3000', '1<<10000']
runner=pyperf.Runner()
forcincases:
i=eval(c)
m=mpz(i)
runner.bench_func(c, int, m)
# bench-import.pyimportosimportpyperf_T=os.getenv('_T')
if_T=="gmpy2.mpz":
fromgmpy2importmpzelif_T=="gmp.mpz":
fromgmpimportmpzelse:
fromflintimportfmpzasmpzcases= ['1<<7', '1<<38', '1<<300', '1<<3000', '1<<10000']
runner=pyperf.Runner()
forcincases:
i=eval(c)
runner.bench_func(c, mpz, i)

@oscarbenjamin

Copy link
Copy Markdown
Collaborator

conditional compilation seems to be deprecated by Cython.

The way to do it is to move the conditional compilation to C e.g.:

cdef extern from*:
"""
/*
* fmpz_mod_mat function signatures were changed in FLINT 3.1.0
*/
#if __FLINT_RELEASE >= 30100 /* Flint 3.1.0 or later */
#define compat_fmpz_mod_mat_init(mat, rows, cols, ctx) fmpz_mod_mat_init(mat, rows, cols, ctx)
#define compat_fmpz_mod_mat_init_set(mat, src, ctx) fmpz_mod_mat_init_set(mat, src, ctx)
#define compat_fmpz_mod_mat_clear(mat, ctx) fmpz_mod_mat_clear(mat, ctx)
#define compat_fmpz_mod_mat_set(A, B, ctx) fmpz_mod_mat_set(A, B, ctx)

@oscarbenjamin

Copy link
Copy Markdown
Collaborator

To my surprise, #324 approach seems to be better.

Is there anything that can improve this either in CPython or GMP?

Or should we just conclude that for python-flint the approach in gh-324 is best?

The approach in gh-324 seems simpler and works for all supported CPython versions without needing any conditional compilation.

What are gmpy2 or python-gmp going to use?

@skirpichev

Copy link
Copy Markdown
Author

The way to do it is to move the conditional compilation to C e.g.

Yes, makes sense. I think it should be possible to expose simple wrappers like gmpy2's mpz_set_PyLong(), which will have all needed fallbacks.

Is there anything that can improve this either in CPython or GMP?

I don't think that something can be impoved on CPython side, at least not for "big enough" integers. The PEP 757 API provides raw access to the "array of digits" view for CPython int's.

Though, maybe mpz_import/export() could be improved.

What are gmpy2 or python-gmp going to use?

Unfortunately, #324 approach can't be translated to C easily for all supported CPython versions. Probably, I'll keep the current solution, but will benchmark also new approach (PyLong_AsNativeBytes/PyLong_FromNativeBytes).

@oscarbenjamin

Copy link
Copy Markdown
Collaborator

maybe mpz_import/export() could be improved.

Without having looked at the code my guess would be that because these functions are quite general they end up being less efficient than PyLong_AsNativeBytes/PyLong_FromNativeBytes when dealing specificially with CPython's 30 bit limb format.

@oscarbenjamin

Copy link
Copy Markdown
Collaborator

Okay thanks @skirpichev for the benchmarks here. This PR was useful but I think what it proves is that we should go with gh-324 instead.

@oscarbenjamin

Copy link
Copy Markdown
Collaborator

This PR was useful but I think what it proves is that we should go with gh-324 instead.

Actually perhaps it suggests that we should not use mpz_import/mpz_export but it might still be faster to use PEP 757 with some custom C code that is specialised for the 30 bit limb case.

@skirpichev
skirpichev deleted the pep757-conversion/159 branch January 24, 2026 23:47
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

More efficient conversion from Python int to fmpz

2 participants

@skirpichev@oscarbenjamin