3

Есть ли способ для профиля использования процесса python GIL? В принципе, я хочу найти , какой процент времени удерживать GIL. Процесс является однопоточным.Профилирование GIL

Мотивация заключается в том, что у меня есть код, написанный в Cython, который использует nogil. В идеале я хотел бы запустить его в многопоточном процессе, но для того, чтобы узнать, может ли это быть хорошей идеей, мне нужно знать, свободен ли GIL значительную часть времени.


Я нашел this related question, от 8 лет назад. Единственный ответ - «Нет». Надеюсь, с тех пор все изменилось.

+0

Я видел на одном из разговоров Дэвида Бейсли о 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/native-код вызывается и измеряется, чтобы увидеть, вы получаете какие-либо улучшения.

В зависимости от используемого варианта многопроцессорность может работать в случаях, которые связаны главным образом с процессором. Многопроцессорность добавляет накладные расходы, поэтому, как правило, это хороший подход для задач с привязкой к ЦП, которые сохраняются относительно долго (несколько секунд или более).

+1

Помимо первого предложения, это не отвечает на вопрос. В частности, поскольку этот ответ игнорирует ситуацию, когда многопоточность будет полезна без каких-либо операций ввода-вывода, несмотря на то, что OP явно упоминает об этом. В частности, нет никакой причины запуска кода cython с nogil в нескольких процессах вместо нескольких потоков. – Voo

+0

В какой ситуации многопоточность помогла бы, если бы не был задействован IO или собственный (или, скорее, не-Python) код? Кстати, я не вижу в этом вопроса упоминания о IO. –

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