dpdani / cereggii

Thread synchronization utilities for Python
Apache License 2.0
9 stars 0 forks source link

AtomicInt #7

Closed dpdani closed 8 months ago

dpdani commented 9 months ago

comparing the following programs, AtomicInt provides an easy way to achieve correctness, with the nice feature of being lock-free. using AtomicInt.get_handle() provides great performance improvements.

also see colesbury/nogil#121

$ python
Python 3.9.10 (main, Nov  8 2023, 12:16:13)
[nogil, GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 

import threading

class Spam:
    def __init__(self):
        self.counter = 0

spam = Spam()
print(f"{spam.counter=}")

def incr():
    global spam
    for _ in range(5_000_000):
        spam.counter += 1

threads = [threading.Thread(target=incr) for _ in range(3)]

for t in threads:
    t.start()

for t in threads:
    t.join()

print(f"{spam.counter=}")
$ /bin/time -p python ./atomic_int_0.py
spam.counter=0
spam.counter=5079663
real 62.42
user 34.62
sys 31.22
$ PYTHONGIL=1 /bin/time -p python ./atomic_int_0.py
spam.counter=0
spam.counter=15000000
real 1.22
user 1.21
sys 0.01

import threading

import cereggii

class Spam:
    def __init__(self):
        self.counter = cereggii.AtomicInt(0)

spam = Spam()
print(f"{spam.counter.get()=}")

def incr():
    global spam
    for _ in range(5_000_000):
        spam.counter += 1

threads = [threading.Thread(target=incr) for _ in range(3)]

for t in threads:
    t.start()

for t in threads:
    t.join()

print(f"{spam.counter.get()=}")
$ /bin/time -p python ./atomic_int_1.py
spam.counter.get()=0
spam.counter.get()=15000000
real 56.53
user 26.61
sys 31.90

import threading

import cereggii

class Spam:
    def __init__(self):
        self.counter = cereggii.AtomicInt(0)

spam = Spam()
print(f"{spam.counter.get()=}")

def incr():
    global spam
    h = spam.counter.get_handle()
    for _ in range(5_000_000):
        h += 1

threads = [threading.Thread(target=incr) for _ in range(3)]

for t in threads:
    t.start()

for t in threads:
    t.join()

print(f"{spam.counter.get()=}")
$ /bin/time -p python ./atomic_int_2.py
spam.counter.get()=0
spam.counter.get()=15000000
real 2.23
user 6.55
sys 0.00