[issue14478] Decimal hashing very slow, could be cached

Serhiy Storchaka report at bugs.python.org
Tue Apr 10 18:10:40 CEST 2012


Serhiy Storchaka <storchaka at gmail.com> added the comment:

> The patch for the Python version looks good to me

Oh, but used by James Hutchison approach is faster. When I disable the
_decimal:

Unpatched:
int:  2.24056077003479
CachingDecimal:  8.49468207359314
Decimal:  187.68132972717285

With rhettinger's LBYL patch:
int:  2.1670639514923096
CachingDecimal:  8.790924310684204
Decimal:  10.426796436309814

With EAFP patch:
int:  2.1392786502838135
CachingDecimal:  8.431122303009033
Decimal:  8.263015270233154

----------
Added file: http://bugs.python.org/file25169/decimal_hash_2.patch

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue14478>
_______________________________________
-------------- next part --------------
diff -r a012d5df2c73 Lib/decimal.py
--- a/Lib/decimal.py	Tue Apr 10 16:27:58 2012 +0200
+++ b/Lib/decimal.py	Tue Apr 10 18:46:19 2012 +0300
@@ -547,7 +547,7 @@
 class Decimal(object):
     """Floating point class for decimal arithmetic."""
 
-    __slots__ = ('_exp','_int','_sign', '_is_special')
+    __slots__ = ('_exp','_int','_sign', '_is_special', '_hash')
     # Generally, the value of the Decimal instance is given by
     #  (-1)**_sign * _int * 10**_exp
     # Special values are signified by _is_special == True
@@ -983,6 +983,10 @@
 
     def __hash__(self):
         """x.__hash__() <==> hash(x)"""
+        try:
+            return self._hash
+        except AttributeError:
+            pass
 
         # In order to make sure that the hash of a Decimal instance
         # agrees with the hash of a numerically equal integer, float
@@ -992,20 +996,22 @@
             if self.is_snan():
                 raise TypeError('Cannot hash a signaling NaN value.')
             elif self.is_nan():
-                return _PyHASH_NAN
+                hash_ = _PyHASH_NAN
             else:
                 if self._sign:
-                    return -_PyHASH_INF
+                    hash_ = -_PyHASH_INF
                 else:
-                    return _PyHASH_INF
-
-        if self._exp >= 0:
-            exp_hash = pow(10, self._exp, _PyHASH_MODULUS)
+                    hash_ = _PyHASH_INF
         else:
-            exp_hash = pow(_PyHASH_10INV, -self._exp, _PyHASH_MODULUS)
-        hash_ = int(self._int) * exp_hash % _PyHASH_MODULUS
-        ans = hash_ if self >= 0 else -hash_
-        return -2 if ans == -1 else ans
+            if self._exp >= 0:
+                exp_hash = pow(10, self._exp, _PyHASH_MODULUS)
+            else:
+                exp_hash = pow(_PyHASH_10INV, -self._exp, _PyHASH_MODULUS)
+            hash_ = int(self._int) * exp_hash % _PyHASH_MODULUS
+            if self < 0: hash_ = -hash_
+            if hash_ == -1: hash_ = -2
+        self._hash = hash_
+        return hash_
 
     def as_tuple(self):
         """Represents the number as a triple tuple.


More information about the Python-bugs-list mailing list