Last active
April 4, 2025 12:27
-
-
Save x42005e1f/149d3994d5f7bd878def71d5404e6ea4 to your computer and use it in GitHub Desktop.
One of aiologic's most impressive benchmarks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| # SPDX-FileCopyrightText: 2024 Ilya Egorov <[email protected]> | |
| # SPDX-License-Identifier: 0BSD | |
| import sys | |
| import time | |
| import threading | |
| from concurrent.futures import ThreadPoolExecutor, wait | |
| import aiologic | |
| def main(): | |
| value, threads = 1, 1 | |
| while threads != 17711: | |
| print(f"threads = {threads}, value = {value}:") | |
| ops = [] | |
| for cls in (aiologic.Semaphore, threading.Semaphore): | |
| name = f"{cls.__module__.partition('.')[0]}.{cls.__name__}" | |
| sem = cls(value) | |
| num = [0] * threads | |
| start = 0 | |
| stop = 0 | |
| barrier = aiologic.Latch(threads + 1) | |
| def func(i): | |
| barrier.wait() | |
| n = 0 | |
| while not stop: | |
| with sem: | |
| n += 1 | |
| num[i] = n | |
| with ThreadPoolExecutor(threads) as executor: | |
| futures = [executor.submit(func, i) for i in range(threads)] | |
| barrier.wait() # ensure that no threads are sleeping | |
| start = time.monotonic() | |
| try: | |
| time.sleep(6) | |
| finally: | |
| stop = time.monotonic() | |
| wait(futures) | |
| total_num = sum(num) | |
| average_num = total_num / len(num) | |
| count = total_num / (stop - start) | |
| if threads == 1: | |
| fairness = 1 | |
| else: | |
| fairness = 1 - ( | |
| sum(abs(average_num - i) for i in num) | |
| / (2 * (total_num - average_num)) | |
| ) | |
| print(end=f" {name + ':': <20} {count: >10.0f} ops") | |
| print(end=f" {fairness * 100: >5.2f}% fairness") | |
| if unused := num.count(0): | |
| if unused == 1: | |
| print(end=" 1 unused thread") | |
| else: | |
| print(end=f" {unused} unused threads") | |
| print() | |
| ops.append(count) | |
| if abs(ops[0] - ops[1]) > 0.05: | |
| print(" ") | |
| if ops[0] >= ops[1]: | |
| print(f" {ops[0] / ops[1]:.1f}x speedup!") | |
| else: | |
| print(f" {ops[1] / ops[0]:.1f}x slowdown!") | |
| print() | |
| value, threads = threads, value + threads | |
| if __name__ == "__main__": | |
| sys.exit(main()) |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is a performance benchmark that compares
aiologic.Semaphoreandthreading.Semaphorein terms of operations per second. It considers the case of value ≈ threads / 2, because this is the case whereaiologic.Semaphoreperforms the best andthreading.Semaphoreperforms the worst when running on PyPy:Fun fact: even 15161 ops is slower than pinging localhost!