GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Dramatic slowdown with random.random on free-threading build. · Issue #118392 · python/cpython

Dramatic slowdown with random.random on free-threading build. #118392

New issue
Copy link
New issue
Copy link
Open
Open
Dramatic slowdown with random.random on free-threading build.#118392
Copy link
Labels
performancePerformance or resource usagePerformance or resource usagetopic-free-threading

Description

@corona10
corona10
opened on Apr 29, 2024
Issue body actions

out

This slowdown was found with one of my favorite benchmarks, which is calculating the pi value with the Monte Carlo method.

import os
import random
import time
from threading import Thread

def monte_carlo_pi_part(n: int, idx: int, results: list[int]) -> None:
    count = 0
    for i in range(n):
        x = random.random()
        y = random.random()

        if x*x + y*y <= 1:
            count += 1
    results[idx] = count


n = 10000
threads = []
num_threads = 100
results = [0] * num_threads
a = time.time()
for i in range(num_threads):
    t = Thread(target=monte_carlo_pi_part, args=(n, i, results))
    t.start()
    threads.append(t)

while threads:
    t = threads.pop()
    t.join()

b = time.time()
print(sum(results) / (n * num_threads) * 4)
print(b-a)

Acquiring critical sections for random methods causes this slowdown.
Removing @critical_section from the method, which uses genrand_uint32 and then updating genrand_uint32 to use atomic operation makes the performance acceptable.

Build Elapsed PI
Default (with specialization) 0.16528010368347168 3.144508
Free-threading (with no specialization) 0.548654317855835 3.1421
Free-threading with my patch (with no specialization) 0.2606849670410156 3.141108

Linked PRs

  • gh-118392: Update genrand_uint32 to use atomic operation #118393
  • gh-118392: Add note about random.random for multi thread app #118396

Activity

  1. corona10
    added
    topic-free-threading
    on Apr 29, 2024
  2. corona10 commented on Apr 29, 2024

    @corona10
    corona10
    on Apr 29, 2024
    MemberAuthor
    More actions

    cc @colesbury

  3. corona10
    added
    performancePerformance or resource usagePerformance or resource usage
    on Apr 29, 2024
  4. corona10
    added a commit that references this issue on Apr 29, 2024

    pythongh-118392: Update genrand_uint32 to use atomic operation

    d1cf0bf
  5. colesbury commented on Apr 29, 2024

    @colesbury
    colesbury
    on Apr 29, 2024
    Contributor
    More actions

    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 Random objects 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 Hennoste
  6. corona10 commented on Apr 29, 2024

    @corona10
    corona10
    on Apr 29, 2024
    MemberAuthor
    More actions

    In 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.

  7. corona10 commented on Apr 29, 2024

    @corona10
    corona10
    on Apr 29, 2024
    MemberAuthor
    More actions

    #118393 (comment)

  8. corona10
    added a commit that references this issue on Apr 29, 2024

    pythongh-118392: Add warning about random.random for multi thread app

    134a82c
  9. colesbury commented on Apr 29, 2024

    @colesbury
    colesbury
    on Apr 29, 2024
    Contributor
    More actions

    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
  10. corona10
    added a commit that references this issue on Apr 30, 2024

    gh-118392: Add note about random.random for multi thread app (gh-118396)

    11cbf77
  11. SonicField
    added 2 commits that reference this issue on May 8, 2024

    pythongh-118392: Add note about random.random for multi thread app (p…

    09ecc79

    pythongh-118392: Add note about random.random for multi thread app (p…

    c8b8a95
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    performancePerformance or resource usagePerformance or resource usagetopic-free-threading

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.