Skip to content

Python 3.13.0 No-GIL Resulting In Incorrect Reduced Results Occasionally. #125054

@leimao

Description

@leimao

Bug report

Bug description:

Occasionally, running the following two scripts with PYTHON_GIL=0 using Python 3.13.0 would result in incorrect reduced results on my machine. I also used a Docker container for building and reproducing results, please refer to my article for more details if necessary.

import math
import queue
import threading
import time


def compute_partial_factorial(start, end):

    partial_factorial = 1
    for i in range(start, end):
        partial_factorial *= i
    return partial_factorial


def compute_factorial_multithread(num, num_threads):

    threads = []
    results = queue.Queue()
    chunk_size = num // num_threads
    for i in range(num_threads):
        start = i * chunk_size + 1
        end = num + 1 if i == num_threads - 1 else (i + 1) * chunk_size + 1
        thread = threading.Thread(
            target=lambda: results.put(compute_partial_factorial(start, end)))
        thread.start()
        threads.append(thread)
    for thread in threads:
        thread.join()
    total_factorial = 1
    while not results.empty():
        total_factorial *= results.get()
    return total_factorial


def main():

    num_repeats = 20
    num = 100000
    factorial_ground_truth = math.factorial(num)
    num_threads = 4
    factorial_multithread = compute_factorial_multithread(num, num_threads)
    assert factorial_ground_truth == factorial_multithread
    # Time the factorial computation
    start_time = time.time()
    for _ in range(num_repeats):
        factorial_multithread = compute_factorial_multithread(num, num_threads)
    end_time = time.time()
    print(
        f"Average Time Elapsed: {(end_time - start_time) / num_repeats:.2f}s")


if __name__ == "__main__":

    main()
PYTHON_GIL=0 python factorial.py
import queue
import threading
import time


def compute_sum(arr, start, end):

    sum = 0
    for i in range(start, end):
        sum += arr[i]
    return sum


def compute_sum_multithread(arr, num_threads):

    n = len(arr)
    chunk_size = n // num_threads
    threads = []
    results = queue.Queue()
    for i in range(num_threads):
        start = i * chunk_size
        end = n if i == num_threads - 1 else (i + 1) * chunk_size
        thread = threading.Thread(
            target=lambda: results.put(compute_sum(arr, start, end)))
        thread.start()
        threads.append(thread)
    for thread in threads:
        thread.join()
    sum = 0
    while not results.empty():
        sum += results.get()
    return sum


def main():

    num_repeats = 20
    num_elements = 10000000
    arr = list(range(1, num_elements + 1))
    sum_ground_truth = num_elements * (num_elements + 1) // 2
    num_threads = 4
    sum_multithread = compute_sum_multithread(arr, num_threads)
    assert sum_ground_truth == sum_multithread
    # Time the sum computation
    start_time = time.time()
    for _ in range(num_repeats):
        sum_multithread = compute_sum_multithread(arr, num_threads)
    end_time = time.time()
    print(
        f"Average Time Elapsed: {(end_time - start_time) / num_repeats:.2f}s")


if __name__ == "__main__":

    main()
PYTHON_GIL=0 python reduce_sum.py

CPython versions tested on:

3.13

Operating systems tested on:

Linux

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions