3

有没有办法来配置python进程使用GIL?基本上,我想找出GIL持有的时间百分比为。该过程是单线程的。分析GIL

我的动机是我有一些用Cython编写的代码,它使用nogil。理想情况下,我想在多线程的过程中运行它,但为了知道这可能是一个好主意,我需要知道GIL是否有大量时间空闲。


我发现this related question,从8年前。唯一的答案是“否”。希望之后,事情发生了变化。

+0

我在David Beazley的GIL会谈中看到他如何描述GIL:http://www.dabeaz。com/GIL/ – denfromufa

+0

@denfromufa如果我的问题的答案是在这个链接的某个地方,我会很感激,如果你张贴它作为答案。 – shx2

回答

1

完全是偶然的,我发现了一个工具,它只是这样做的:gil_load

它实际上是之后我发布了这个问题。

干得好,@chrisjbillington。

>>> import sys, math 
>>> import gil_load 
>>> gil_load.init() 
>>> gil_load.start(output = sys.stdout) 
>>> for x in range(1, 1000000000): 
...  y = math.log(x**math.pi) 
[2017-03-15 08:52:26] GIL load: 0.98 (0.98, 0.98, 0.98) 
[2017-03-15 08:52:32] GIL load: 0.99 (0.99, 0.99, 0.99) 
[2017-03-15 08:52:37] GIL load: 0.99 (0.99, 0.99, 0.99) 
[2017-03-15 08:52:43] GIL load: 0.99 (0.99, 0.99, 0.99) 
[2017-03-15 08:52:48] GIL load: 1.00 (1.00, 1.00, 1.00) 
[2017-03-15 08:52:52] GIL load: 1.00 (1.00, 1.00, 1.00) 
<...> 

>>> import sys, math 
>>> import gil_load 
>>> gil_load.init() 
>>> gil_load.start(output = sys.stdout) 
>>> for x in range(1, 1000000000): 
...  with open('/dev/null', 'a') as f: 
...   print(math.log(x**math.pi), file=f) 

[2017-03-15 08:53:59] GIL load: 0.76 (0.76, 0.76, 0.76) 
[2017-03-15 08:54:03] GIL load: 0.77 (0.77, 0.77, 0.77) 
[2017-03-15 08:54:09] GIL load: 0.78 (0.78, 0.78, 0.78) 
[2017-03-15 08:54:13] GIL load: 0.80 (0.80, 0.80, 0.80) 
[2017-03-15 08:54:19] GIL load: 0.81 (0.81, 0.81, 0.81) 
[2017-03-15 08:54:23] GIL load: 0.81 (0.81, 0.81, 0.81) 
[2017-03-15 08:54:28] GIL load: 0.81 (0.81, 0.81, 0.81) 
[2017-03-15 08:54:33] GIL load: 0.80 (0.80, 0.80, 0.80) 
<...> 
-1

我不知道这样的工具。

但是有一些启发式方法可以帮助您猜测多线程是否会有所帮助。正如您可能知道的那样,GIL将在IO操作期间发布,并且一些调用本地代码,尤其是第三方本机模块。如果你没有那么多的代码,那么多线程很可能不会帮你。

如果你有IO /本地代码,那么你可能不得不试试它。根据代码的不同,转换整个事情以利用多个线程可能需要很多工作,所以您可能想尝试将多线程应用到您知道IO /本机代码正在调用的部分,然后测量以查看是否你会得到任何改进。

根据您的使用情况,多处理可能适用于主要受CPU限制的情况。多处理确实会增加开销,所以对于持续相对较长时间(几秒或更长时间)的CPU绑定任务而言,这是一种很好的方法。

+1

除了第一句话,这并不回答这个问题。特别是因为这个答案忽略了一种情况,即尽管OP明确提到了多线程,但没有任何IO发生。特别是没有任何理由在多进程而不是多线程下使用nogil运行cython代码。 – Voo

0

如果您想知道GIL采取了多少次,您可以使用gdb断点。例如:

> cat gil_count_example.py 
import sys 
import threading 
from threading import Thread 

def worker(): 
    k=0 
    for j in range(10000000): 
     k+=j 
    return 

num_threads = int(sys.argv[1]) 
threads = [] 
for i in range(num_threads): 
    t = Thread(target = worker) 
    t.start() 
    threads.append(t) 

for t in threads: 
    t.join() 

对于3.X突破上take_gil

 
> cgdb --args python3 gil_count_example.py 8 
(gdb) b take_gil 
(gdb) ignore 1 100000000 
(gdb) r 
(gdb) info breakpoints 
Num  Type   Disp Enb Address   What 
1  breakpoint  keep y 0x00007ffff7c85f10 in take_gil 
                at Python-3.4.3/Python/ceval_gil.h:208 
     breakpoint already hit 1886 times 

对于2.X突破上PyThread_acquire_lock

 
> cgdb --args python2 gil_count_example.py 8 
(gdb) b PyThread_acquire_lock 
(gdb) ignore 1 100000000 
(gdb) r 
(gdb) info breakpoints 
Num  Type   Disp Enb Address   What 
    1  breakpoint  keep y 0x00000039bacfd410 
     breakpoint already hit 1584561 times 

高效的穷人的探查也可用于配置文件花在功能上的时间,我用https://github.com/knielsen/knielsen-pmp

 
> ./get_stacktrace --max=100 --freq=10 `/sbin/pidof python2` 
... 
292 71.92% sem_wait:PyThread_acquire_lock 

 
> ./get_stacktrace --max=100 --freq=10 `/sbin/pidof python3` 
... 
557 77.68% pthread_cond_timedwait:take_gil