๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ Python

[Python] GIL์„ ํ•ด์ œํ•ด๋ณด์ž

by dev.py 2024. 10. 11.

๋“œ๋””์–ด Python 3.13.0 ์—์„œ GIL์ด ํ•ด์ œ ๋˜์—ˆ๋‹ค.

FREEEEEE threaded CPython

 

Python 3.13.0 ์œผ๋กœ ๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋”ฉ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋ณด๋ ค ํ–ˆ์ง€๋งŒ, ์‹คํŒจํ–ˆ๋‹ค.

GIL์ด ํ•ด์ œ๋˜์ง€ ์•Š์€ ๋ชจ์Šต

์ฐพ์•„๋ณด๋‹ˆ, ์ผ๋ฐ˜ CPython 3.13 ์—์„  ์•„์ง ์•ˆ๋˜๊ณ , GIL์„ ํ•ด์ œํ•˜์—ฌ ๋นŒ๋“œ ๋œ CPython์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

pyenv ๋ฅผ ํ™œ์šฉํ•˜์—ฌ GIL์ด ํ•ด์ œ๋œ CPython์„ ๊ตฌํ•ด๋ณด์ž

pyenv install 3.13t-dev

pyenv shell 3.13t-dev

python3 --version 
# Python 3.13.0+ ํ˜•์‹์œผ๋กœ ๋‚˜์˜ค๋ฉด ์„ฑ๊ณต

 

 

 

GIL์ด ํ•ด์ œ๋˜์–ด ์„ฑ๋Šฅ์ด ์‹ฑ๊ธ€ ์“ฐ๋ ˆ๋“œ < ๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์‹ฑ < ๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋“œ ์ˆœ์œผ๋กœ ๋‚˜์™”๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด 8์ฝ”์–ด 8์“ฐ๋ ˆ๋“œ M3 ๊ธฐ์ค€ CPU๋ฅผ ์ž˜ ๊ฐˆ๊ตฌ๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž

8์“ฐ๋ ˆ๋“œ, 8ํ”„๋กœ์„ธ์Šค ๊นŒ์ง€ ์ž˜ ๊ฐˆ๊ตฌ๋Š” ๋ชจ์Šต์ด๋‹ค.

 

 

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

import math
import multiprocessing
import sys
import sysconfig
import threading
import time

PYTHON_GIL = 1

# A CPU-bound task: computing a large number of prime numbers
def is_prime(n: int) -> bool:
    if n <= 1:
        return False
    for i in range(2, int(math.sqrt(n)) + 1):
        if n % i == 0:
            return False
    return True


def count_primes(start: int, end: int) -> int:
    count = 0
    for i in range(start, end):
        if is_prime(i):
            count += 1
    return count


def threaded_count_primes(n: int, num_threads: int) -> int:
    threads = []
    results = [0] * num_threads

    def worker(start: int, end: int, index: int) -> None:
        results[index] = count_primes(start, end)

    step = n // num_threads
    for i in range(num_threads):
        start = i * step
        end = (i + 1) * step if i != num_threads - 1 else n
        thread = threading.Thread(target=worker, args=(start, end, i))
        threads.append(thread)
        thread.start()

    for thread in threads:
        thread.join()

    return sum(results)


def multiprocess_count_primes(n: int, num_processes: int) -> int:
    with multiprocessing.Pool(processes=num_processes) as pool:
        step = n // num_processes
        tasks = [
            (i * step, (i + 1) * step if i != num_processes - 1 else n)
            for i in range(num_processes)
        ]
        results = [pool.apply_async(count_primes, args=task) for task in tasks]
        return sum([result.get() for result in results])


def main() -> None:
    # print(f"The GIL active: {sys._is_gil_enabled()}")
    print(f"Version of python: {sys.version}")

    active = sysconfig.get_config_vars().get("Py_GIL_DISABLED")

    if active is None:
        print("GIL cannot be disabled")
    if active == 0:
        print("GIL is [active]")
    if active == 1:
        print("GIL is [disabled]")

    N = 3*10**6


    start_time = time.time()
    single_threaded_result = count_primes(0, N)
    single_threaded_time = time.time() - start_time
    print(
        f"Single-threaded: {single_threaded_result} primes in [{single_threaded_time:.2f}] seconds"
    )

    for i in range(1, 16):
        NUM_THREADS = i
        NUM_PROCESSES = i

        start_time = time.time()
        threaded_result = threaded_count_primes(N, NUM_THREADS)
        threaded_time = time.time() - start_time
        print(f"{i} Threaded: {threaded_result} primes in [{threaded_time:.2f}] seconds")

        start_time = time.time()
        multiprocess_result = multiprocess_count_primes(N, NUM_PROCESSES)
        multiprocess_time = time.time() - start_time
        print(
            f"{i} Multiprocessed: {multiprocess_result} primes in [{multiprocess_time:.2f}] seconds"
        )
        print()

if __name__ == "__main__":
    main()

์›๋ณธ ์ถœ์ฒ˜ :  https://github.com/ArjanCodes/examples/blob/main/2024/gil/main.py