-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
bpo-37863: Optimize Fraction.__hash__() #15298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow - that was fast! 😉 Just left a comment about a simplification.
dinv = pow(self._denominator, _PyHASH_MODULUS - 2, _PyHASH_MODULUS) | ||
try: | ||
dinv = pow(self._denominator, -1, _PyHASH_MODULUS) | ||
except ValueError: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In what case ValueError
is raised?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ValueError
is raised if and only if _PyHASH_MODULUS
divides self._denominator
. There is no modular inverse if and only if the gcd of the base and the modulus is non-trivial (not 1), but the modulus is a prime in this case, so in this case that's the same as the base being a multiple of the modulus.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you. We need a test for this.
Any benchmarking results? |
Baseline:
Patched
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a test for the denominator being a multiple of the modulus.
dinv = pow(self._denominator, _PyHASH_MODULUS - 2, _PyHASH_MODULUS) | ||
try: | ||
dinv = pow(self._denominator, -1, _PyHASH_MODULUS) | ||
except ValueError: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you. We need a test for this.
When you're done making the requested changes, leave the comment: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a suggestion for something else to try, avoiding the remaining division (mod) at the cost of another hash()
call. At best minor, but possibly significant.
hash_ = _PyHASH_INF | ||
else: | ||
hash_ = abs(self._numerator) * dinv % _PyHASH_MODULUS | ||
result = hash_ if self >= 0 else -hash_ | ||
hash_ = hash(abs(self._numerator)) * dinv % _PyHASH_MODULUS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another possibility is to write this as
hash_ = hash(hash(abs(self._numerator)) * dinv)
to avoid the (in general) 122-by-61 bit %
division. The int hash should compute the same thing, but doesn't do any division (it's all bit rotations and adds, one pair per internal int "digit"). (While the docs don't point this out, reduction by _PyHASH_MODULUS
in binary is akin to "casting out nines" in decimal to find a remainder mod 9 - no division is really needed.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great!
@rhettinger: Please replace |
https://bugs.python.org/issue37863