Repository navigation
Dramatic slowdown with random.random on free-threading build. #118392
Description
Activity
cc @colesbury
- added a commit that references this issue
on Apr 29, 2024 It'd be great to have the default random number generator be safe and efficient when called from multiple threads, but I think that's a bigger change that requires more discussion and maybe a PEP.
In the meantime, I think the advice should be to use individual
Randomobjects per-thread. In other words, the program should be written as:def monte_carlo_pi_part(n: int, idx: int, results: list[int]) -> None: r = random.Random() count = 0 for i in range(n): x = r.random() y = r.random() if x*x + y*y <= 1: count += 1 results[idx] = count
I think that you also would need #118112 (or deferred reference counting) for the program to be efficient, or otherwise the reference count contention is going to be a bottleneck.
Reacted by Donghee Na, Joongi Kim, Erlend E. Aasland and Juhan Oskar HennosteIn the meantime, I think the advice should be to use individual Random objects per-thread. In other words, the program should be written as:
Yeah, it works. so it should be included somewhere in the migration guide.
We could also consider adopting a strategy like Go does, at least for the free-threaded build:
- If
seed()has not been called, use a fast per-thread RNG for the global calls - Once
seed()is called, switch to the shared state RNG to preserve old behavior.
Reacted by Donghee Na and Erlend E. Aasland- If
This slowdown was found with one of my favorite benchmarks, which is calculating the pi value with the Monte Carlo method.
Acquiring critical sections for random methods causes this slowdown.
Removing
@critical_sectionfrom the method, which usesgenrand_uint32and then updatinggenrand_uint32to use atomic operation makes the performance acceptable.Linked PRs