Use CoW pattern to manage constant_memo() cache

Closes #1135
This commit is contained in:
Sergey B Kirpichev
2026-07-19 10:28:41 +03:00
parent 6309ea0550
commit 65dca57ca7
2 changed files with 37 additions and 8 deletions
+8 -8
View File
@@ -77,6 +77,7 @@ for k in range(1, LOG_TAYLOR_PREC.bit_length()+1):
# #
#----------------------------------------------------------------------------#
def constant_memo(f):
"""
Decorator for caching computed values of mathematical
@@ -84,16 +85,15 @@ def constant_memo(f):
function taking a single argument prec as input and
returning a fixed-point value with the given precision.
"""
f.memo_prec = -1
f.memo_val = None
f._prec_val = -1, None
def g(prec, **kwargs):
memo_prec = f.memo_prec
memo_prec, memo_val = f._prec_val
if prec <= memo_prec:
return f.memo_val >> (memo_prec-prec)
newprec = int(prec*1.05+10)
f.memo_val = f(newprec, **kwargs)
f.memo_prec = newprec
return f.memo_val >> (newprec-prec)
return memo_val >> (memo_prec-prec)
memo_prec = int(prec*1.05+10)
memo_val = f(memo_prec, **kwargs)
f._prec_val = memo_prec, memo_val
return memo_val >> (memo_prec-prec)
g.__name__ = f.__name__
g.__doc__ = f.__doc__
return g
+29
View File
@@ -4,6 +4,7 @@ import math
import operator
import random
import sys
import threading
from concurrent.futures import ThreadPoolExecutor
import pytest
@@ -783,3 +784,31 @@ def test_eval_repr_roundtrip():
elif x < 0:
x /= 10**n
assert eval(repr(x)) == x, (prec, x)
def test_issue_1135():
for _ in range(100):
n = 4
barrier = threading.Barrier(n)
bad = []
def worker(index):
mp = mpmath.MPContext()
for iteration in range(100):
mp.prec = 100 + 100 * iteration + 10 * index
barrier.wait()
value = float(+mp.pi)
if value != math.pi:
bad.append((mp.prec, value))
threads = [threading.Thread(target=worker, args=(i,))
for i in range(n)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
assert not bad