Skip to content

Instantly share code, notes, and snippets.

@Mononofu
Last active May 5, 2025 20:16
Show Gist options
  • Save Mononofu/f588a4cb194ec5324816cc672c04d3a7 to your computer and use it in GitHub Desktop.
Save Mononofu/f588a4cb194ec5324816cc672c04d3a7 to your computer and use it in GitHub Desktop.
Benchmarking list item access in free-threading Python
from concurrent.futures import ThreadPoolExecutor
import time
import itertools
from collections.abc import Sequence
def get_items(values: Sequence, duration: float):
deadline = time.time() + duration
n = len(values)
for i in itertools.count():
_ = values[i % n]
if i % 100 == 0 and time.time() > deadline:
return i
def decimal_eng(n: float):
if n > 1e9:
return f'{n / 1e9:.1f}G'
elif n > 1e6:
return f'{n / 1e6:.1f}M'
elif n > 1e3:
return f'{n / 1e3:.1f}k'
else:
return f'{n:1.f}'
def measure_all():
duration = 1
num_threads = [1, 2, 4, 8]
length = 100
values = [
lambda i: chr(ord('a') + i) * length,
lambda i: chr(ord('a') + i).encode() * length,
lambda i: bytearray([i] * length),
lambda i: [i] * length,
lambda i: (i,) * length,
lambda i: {k: i for k in range(length)},
]
for make_value in values:
print(f'# {type(make_value(0))}')
print(f' ' * 11, ' shared ', ' separate')
for threads in num_threads:
with ThreadPoolExecutor(threads) as pool:
shared_value = make_value(0)
num_items_shared = sum(
pool.map(lambda _: get_items(shared_value, duration), range(threads)))
num_items_sep = sum(
pool.map(lambda i: get_items(make_value(i), duration), range(threads)))
print(f'{threads:2d} threads: '
f'{decimal_eng(num_items_shared / duration):>6}/sec',
f'{decimal_eng(num_items_sep / duration):>6}/sec')
if __name__ == '__main__':
measure_all()
$ uv run --python 3.14.0a6 concurrent_reads.py
# <class 'str'>
shared separate
1 threads: 19.2M/sec 19.1M/sec
2 threads: 19.2M/sec 19.3M/sec
4 threads: 20.1M/sec 19.6M/sec
8 threads: 23.9M/sec 19.8M/sec
# <class 'bytes'>
shared separate
1 threads: 16.7M/sec 16.6M/sec
2 threads: 16.6M/sec 16.7M/sec
4 threads: 17.2M/sec 16.8M/sec
8 threads: 19.9M/sec 17.6M/sec
# <class 'bytearray'>
shared separate
1 threads: 15.7M/sec 15.7M/sec
2 threads: 15.9M/sec 15.3M/sec
4 threads: 17.0M/sec 16.2M/sec
8 threads: 19.2M/sec 16.4M/sec
# <class 'list'>
shared separate
1 threads: 17.7M/sec 17.6M/sec
2 threads: 17.7M/sec 17.7M/sec
4 threads: 19.0M/sec 18.0M/sec
8 threads: 20.4M/sec 18.9M/sec
# <class 'tuple'>
shared separate
1 threads: 18.0M/sec 18.0M/sec
2 threads: 18.2M/sec 18.1M/sec
4 threads: 19.1M/sec 18.6M/sec
8 threads: 23.4M/sec 19.4M/sec
# <class 'dict'>
shared separate
1 threads: 24.4M/sec 24.5M/sec
2 threads: 24.8M/sec 24.8M/sec
4 threads: 24.0M/sec 24.9M/sec
8 threads: 28.4M/sec 26.3M/sec
$ uv run --python 3.14.0a6+freethreaded concurrent_reads.py
# <class 'bytearray'>
shared separate
1 threads: 15.8M/sec 16.1M/sec
2 threads: 9.7M/sec 29.4M/sec
4 threads: 7.9M/sec 53.4M/sec
8 threads: 5.9M/sec 73.1M/sec
# <class 'str'>
shared separate
1 threads: 17.9M/sec 18.7M/sec
2 threads: 10.3M/sec 35.8M/sec
4 threads: 7.2M/sec 61.5M/sec
8 threads: 3.1M/sec 91.1M/sec
# <class 'bytes'>
shared separate
1 threads: 17.4M/sec 17.6M/sec
2 threads: 9.4M/sec 35.4M/sec
4 threads: 7.2M/sec 65.9M/sec
8 threads: 2.4M/sec 83.4M/sec
# <class 'list'>
shared separate
1 threads: 17.7M/sec 18.7M/sec
2 threads: 8.4M/sec 37.8M/sec
4 threads: 3.8M/sec 76.0M/sec
8 threads: 2.6M/sec 106.2M/sec
# <class 'tuple'>
shared separate
1 threads: 18.9M/sec 19.0M/sec
2 threads: 9.1M/sec 38.2M/sec
4 threads: 3.8M/sec 76.8M/sec
8 threads: 3.0M/sec 100.0M/sec
# <class 'dict'>
shared separate
1 threads: 24.5M/sec 25.2M/sec
2 threads: 11.2M/sec 49.7M/sec
4 threads: 5.7M/sec 91.2M/sec
8 threads: 2.5M/sec 123.3M/sec
$ /Library/Frameworks/PythonT.framework/Versions/3.14/bin/python3t concurrent_reads.py
# <class 'str'>
shared separate
1 threads: 16.4M/sec 16.3M/sec
2 threads: 33.0M/sec 32.5M/sec
4 threads: 52.9M/sec 54.5M/sec
8 threads: 91.6M/sec 85.7M/sec
# <class 'bytes'>
shared separate
1 threads: 16.5M/sec 16.4M/sec
2 threads: 33.4M/sec 33.0M/sec
4 threads: 53.5M/sec 55.0M/sec
8 threads: 78.6M/sec 82.1M/sec
# <class 'bytearray'>
shared separate
1 threads: 15.0M/sec 14.8M/sec
2 threads: 12.4M/sec 30.1M/sec
4 threads: 10.8M/sec 50.0M/sec
8 threads: 7.6M/sec 78.2M/sec
# <class 'list'>
shared separate
1 threads: 17.1M/sec 16.6M/sec
2 threads: 30.2M/sec 29.1M/sec
4 threads: 59.2M/sec 57.0M/sec
8 threads: 79.3M/sec 90.5M/sec
# <class 'tuple'>
shared separate
1 threads: 17.9M/sec 17.9M/sec
2 threads: 35.3M/sec 35.9M/sec
4 threads: 58.5M/sec 61.3M/sec
8 threads: 92.5M/sec 82.4M/sec
# <class 'dict'>
shared separate
1 threads: 21.4M/sec 21.4M/sec
2 threads: 40.9M/sec 41.3M/sec
4 threads: 65.7M/sec 68.6M/sec
8 threads: 107.8M/sec 133.4M/sec
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment