GIL Knocker (gilknocker)
gilknocker is a Python library, with core components implemented in Rust, designed to measure Global Interpreter Lock (GIL) contention in Python applications. It provides a quick and dirty metric by spawning a background thread that periodically attempts to acquire the GIL and records the time taken. This is particularly useful for identifying GIL-bound sections of code without the overhead of a full profiler like `py-spy`. The current version is 0.4.2, and it supports Python 3.9 and newer.
Warnings
- gotcha The `polling_interval_micros` parameter directly impacts both the accuracy of the GIL contention measurement and the performance overhead introduced by `gilknocker`. Setting this value too low increases accuracy but can significantly slow down your program by increasing competition for the GIL. Users should experiment to find an appropriate balance, with `polling_interval_micros=1_000` often suggested as a reasonable starting point.
- gotcha `gilknocker` is designed for quick, specific GIL contention metrics within defined code blocks. It is not intended as a replacement for comprehensive profiling tools like `py-spy` or `cProfile` for general performance analysis, as it focuses solely on GIL activity rather than overall CPU or memory usage.
- gotcha The Python Global Interpreter Lock (GIL) is an evolving part of CPython. Future versions of CPython (e.g., Python 3.13+) are exploring options to make the GIL optional (as proposed by PEP 703), and Python 3.12 introduced a per-interpreter GIL. If the GIL is eventually removed or its internal behavior changes significantly in future Python releases, `gilknocker`'s core functionality may be impacted or become obsolete, requiring updates to remain compatible or relevant.
Install
-
pip install gilknocker
Imports
- Knocker
from gilknocker import Knocker
Quickstart
import time
from gilknocker import Knocker
def my_gil_heavy_function():
# Simulate some GIL-bound work
_ = [i*i for i in range(1_000_000)]
knocker = Knocker(
polling_interval_micros=1000, # How frequently to re-acquire GIL
sampling_interval_micros=10000, # How long to run polling routine
sleeping_interval_micros=100000 # How long to sleep between sampling
)
print("Starting GIL contention measurement...")
knocker.start()
my_gil_heavy_function()
knocker.stop()
print("Stopping GIL contention measurement.")
metrics = knocker.get_metrics()
print(f"Average GIL acquisition time: {metrics.avg_acquisition_time_micros:.2f} microseconds")
print(f"Max GIL acquisition time: {metrics.max_acquisition_time_micros:.2f} microseconds")
print(f"Total samples: {metrics.total_samples}")