From 951bda129a4680ed2e977f9a6ffef323cafb14d1 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 22 Mar 2021 14:01:40 +0300 Subject: [PATCH 1/2] Use Fraction's private attributes in arithmetic methods Adapted from https://github.com/python/cpython/pull/24779/commits/046c84e8f9 That makes arithmetics for small components just as fast as before python/cpython#24779, except for a mixed case (e.g. Fraction + int): On master: $ ./python -m timeit -s 'from fractions import Fraction as F' \ -s 'a = F(7, 8)' -s 'b = F(3, 4)' 'a + b' 20000 loops, best of 5: 12.4 usec per loop $ ./python -m timeit -s 'from fractions import Fraction as F' \ -s 'a = F(7, 8)' -s 'b = 3' 'a + b' 20000 loops, best of 5: 10.3 usec per loop $ ./python -m timeit -s 'from fractions import Fraction as F' \ -s 'a = 3' -s 'b = F(3, 4)' 'a + b' 20000 loops, best of 5: 12.6 usec per loop With the patch: $ ./python -m timeit -s 'from fractions import Fraction as F' \ -s 'a = F(7, 8)' -s 'b = F(3, 4)' 'a + b' 20000 loops, best of 5: 9.98 usec per loop $ ./python -m timeit -s 'from fractions import Fraction as F' \ -s 'a = F(7, 8)' -s 'b = 3' 'a + b' 20000 loops, best of 5: 15.6 usec per loop $ ./python -m timeit -s 'from fractions import Fraction as F' \ -s 'a = 3' -s 'b = F(3, 4)' 'a + b' 20000 loops, best of 5: 16.6 usec per loop --- Lib/fractions.py | 22 ++++++++++--------- ...2-04-23-08-12-14.gh-issue-91851.Jd47V6.rst | 1 + 2 files changed, 13 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2022-04-23-08-12-14.gh-issue-91851.Jd47V6.rst diff --git a/Lib/fractions.py b/Lib/fractions.py index 939741115f9192..d3921154506513 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -395,8 +395,10 @@ class doesn't subclass a concrete type, there's no """ def forward(a, b): - if isinstance(b, (int, Fraction)): + if isinstance(b, Fraction): return monomorphic_operator(a, b) + elif isinstance(b, int): + return monomorphic_operator(a, Fraction(b)) elif isinstance(b, float): return fallback_operator(float(a), b) elif isinstance(b, complex): @@ -409,7 +411,7 @@ def forward(a, b): def reverse(b, a): if isinstance(a, numbers.Rational): # Includes ints. - return monomorphic_operator(a, b) + return fallback_operator(Fraction(a), b) elif isinstance(a, numbers.Real): return fallback_operator(float(a), float(b)) elif isinstance(a, numbers.Complex): @@ -491,8 +493,8 @@ def reverse(b, a): def _add(a, b): """a + b""" - na, da = a.numerator, a.denominator - nb, db = b.numerator, b.denominator + na, da = a._numerator, a._denominator + nb, db = b._numerator, b._denominator g = math.gcd(da, db) if g == 1: return Fraction(na * db + da * nb, da * db, _normalize=False) @@ -507,8 +509,8 @@ def _add(a, b): def _sub(a, b): """a - b""" - na, da = a.numerator, a.denominator - nb, db = b.numerator, b.denominator + na, da = a._numerator, a._denominator + nb, db = b._numerator, b._denominator g = math.gcd(da, db) if g == 1: return Fraction(na * db - da * nb, da * db, _normalize=False) @@ -523,8 +525,8 @@ def _sub(a, b): def _mul(a, b): """a * b""" - na, da = a.numerator, a.denominator - nb, db = b.numerator, b.denominator + na, da = a._numerator, a._denominator + nb, db = b._numerator, b._denominator g1 = math.gcd(na, db) if g1 > 1: na //= g1 @@ -540,8 +542,8 @@ def _mul(a, b): def _div(a, b): """a / b""" # Same as _mul(), with inversed b. - na, da = a.numerator, a.denominator - nb, db = b.numerator, b.denominator + na, da = a._numerator, a._denominator + nb, db = b._numerator, b._denominator g1 = math.gcd(na, nb) if g1 > 1: na //= g1 diff --git a/Misc/NEWS.d/next/Library/2022-04-23-08-12-14.gh-issue-91851.Jd47V6.rst b/Misc/NEWS.d/next/Library/2022-04-23-08-12-14.gh-issue-91851.Jd47V6.rst new file mode 100644 index 00000000000000..a918bffb601b8d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-04-23-08-12-14.gh-issue-91851.Jd47V6.rst @@ -0,0 +1 @@ +Optimize the :class:`~fractions.Fraction` arithmetics for small components. From 5f0ced08eecb841f413595f9c00eef78c6034899 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 29 Apr 2022 05:55:17 +0300 Subject: [PATCH 2/2] Restore monomorphic_operator() --- Lib/fractions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/fractions.py b/Lib/fractions.py index d3921154506513..bdba6c339593c2 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -411,7 +411,7 @@ def forward(a, b): def reverse(b, a): if isinstance(a, numbers.Rational): # Includes ints. - return fallback_operator(Fraction(a), b) + return monomorphic_operator(Fraction(a), b) elif isinstance(a, numbers.Real): return fallback_operator(float(a), float(b)) elif isinstance(a, numbers.Complex):