背景

最近在公司接到了一个监控Python应用的GIL锁竞争性能监测的需求。虽然Python 3.13开始引入了没有GIL锁机制的真多线程并行模式,但毕竟还是试验性功能,且以往的Python版本应用还是占大头

在标准的CPython编译器中,GIL锁是无法通过原生方法获取到数据的,况且就算支持,在Python中运行代码本身就会导致GIL锁的竞争,所以这个方法是行不通的。主流的方法有两种:

      1. 修改CPython源码,自己编译模块

      2. 使用C扩展

但因为是公司的量化需求,修改CPython编译器明显是不现实,也不适合量化的

实现

在Claude老师的帮助下,完成了一个轻量化的C扩展。通过每隔一段很短的时间申请一下GIL锁并立即释放,计算所耗时间来达到监测GIL竞争情况的效果

核心代码主要是:

// 记录开始时间

int64_t t_before = monotonic_ns();

// 尝试获取GIL

PyGILState_STATE gstate = PyGILState_Ensure();

// 记录获取到的时间

int64_t t_after = monotonic_ns();

// 立即释放GIL

PyGILState_Release(gstate);

拿到这个所需时间后的计算方式就因人而异,因地制宜了

小提示

最后再来讲点开发过程中遇到的需要注意的点吧

首先,C扩展需要根据不同的系统/架构/Python大版本编译不同的二进制包。编译好后即可通过Python的import导入了

其次,C扩展自己需要占用一个OS线程(不是Python线程),在我的实现中,我使用了Pthread库。这个库在Linux环境下很好用,但在Windows环境下是没有的,需要自己为MSVC添加pthread-win32库后再做调整

同时,Windows下的C扩展也不支持timespec纳秒和微秒精度;我的解决方案是在Windows环境下使用QueryPerformanceCounter去除以10^9实现的;而微秒则是直接把毫秒数值乘上1000

最后,Windows环境下的编译需求也与Linux环境不同;需要引入windows.h头文件不说,还得使用/O2 /W3 /utf-8 /D_CRT_SECURE_NO_WARNINGS等flag并另外声明libraries=[“pthreadVC2”],作为参考,Linux的编译flag为-std=c11 -O2 -Wall -Wextra -fvisibility=hidden,需要为extra_compile_args和extra_link_args声明-pthread和-lrt