Цей підручник буде зосереджений на одній із важливих тем Python, GIL. Ми також розглянемо, як GIL впливає на продуктивність програм Python за допомогою реалізації коду. Перш ніж заглибитися в цю тему, давайте матимемо базове уявлення про GIL.
перетворення рядка в ціле
GIL або глобальне блокування інтерпретатора
Python Global Interpreter Lock або GIL є важливою частиною багатопотокового програмування. Це тип блокування процесу, який використовується під час роботи з кількома процесами. Він надає керування лише одному потоку. Як правило, Python використовує один потік для запуску одного процесу. Ми отримуємо однаковий результат продуктивності однопотокових і багатопотокових процесів за допомогою GIL. Це обмежує досягнення багатопоточності в Python, оскільки запобігає потокам і працює як один потік.
Примітка. Python не підтримує багатопотоковість, оскільки пакети потоків не дозволяють нам використовувати кілька ядер ЦП.
Чому розробники Python використовують GIL?
Python надає унікальну функцію лічильника посилань, яка використовується для керування пам’яттю. Лічильник посилань підраховує загальну кількість внутрішніх посилань у Python для призначення значення об’єкту даних. Коли кількість посилань досягає нуля, призначена пам'ять об'єкта звільняється. Давайте розглянемо наведений нижче приклад.
приклад -
import sys a = [] b = a sys.getrefcount(a)
Основне занепокоєння щодо змінної підрахунку посилань полягає в тому, що на неї може вплинути, коли два або три потоки намагаються збільшити або зменшити її значення одночасно. Це відоме як стан гонки. Якщо ця умова виникає, це може бути спричинено витоком пам’яті, яка ніколи не звільняється. Він може аварійно завершувати роботу або мати помилки в програмі Python.
GIL допомагає нам усунути таку ситуацію, використовуючи блокування всіх спільних структур даних у потоках, щоб вони не змінювалися непослідовно. Python забезпечує простий спосіб реалізації GIL, оскільки він має справу з потокобезпечним керуванням пам’яттю. GIL вимагає надання єдиного блокування потоку для обробки в Python. Це підвищує продуктивність однопоточної програми, оскільки для обробки потрібно лише одне блокування. Це також допомагає створити будь-яку програму, пов’язану з процесором, і запобігає взаємним блокуванням.
Вплив на багатопотокові програми Python
Існує різниця між обмеженнями ЦП у їх продуктивності та обмеженнями введення/виведення для типової програми Python або будь-якої комп’ютерної програми. Програми, пов’язані з центральним процесором, як правило, розвантажують центральний процесор до своїх меж. Ці програми зазвичай використовуються для математичних обчислень, таких як множення матриць, випалювання, обробка зображень тощо.
Програми, пов’язані з введенням/виведенням, — це ті програми, які витрачають час на отримання вводу/виводу, які можуть бути згенеровані користувачем, файлом, базою даних, мережею тощо. Таким програмам доводиться чекати деякий значний проміжок часу, доки джерело не надасть введення. З іншого боку, джерело також має свій час обробки. Наприклад - користувач думає, що ввести як вхідні дані.
Давайте розберемося в наступному прикладі.
приклад -
import time from threading import Thread COUNT = 100000000 def countdown(num): while num>0: num -= 1 start_time = time.time() countdown(COUNT) end_time = time.time() print('Time taken in seconds -', end_time - start_time)
Вихід:
Time taken in seconds - 7.422671556472778
Тепер ми змінюємо код вище, запускаючи два потоки.
Приклад - 2:
import time from threading import Thread COUNT = 100000000 def countdown(num): while num>0: num -= 1 thread1 = Thread(target=countdown, args=(COUNT//2,)) thread2 = Thread(target=countdown, args=(COUNT//2,)) start_time = time.time() thread1.start() thread2.start() thread1.join() thread2.join() end_time = time.time() print('Time taken in seconds -', end_time - start_time)
Вихід:
Time taken in seconds - 6.90830135345459
Як ми бачимо, для завершення обох кодів знадобився однаковий час. GIL запобіг паралельному виконанню потоків, пов’язаних з ЦП, у другому коді.
Чому GIL ще не видалено?
Багато програмістів скаржаться на це, але Python не може внести такі зміни, як видалення GIL. Інша причина полягає в тому, що GIL на даний момент не покращено. Якщо це зміниться в Python 3, це створить серйозні проблеми. Замість видалення GIL можна покращити концепцію GIL. За словами Гвідо ван Россома -
«Я б вітав набір патчів у Py3k, лише якщо продуктивність однопоточної програми (і багатопоточної, але пов’язаної з введенням/виведенням програми) не зменшується».
Існує також багато доступних методів, які вирішують ту саму проблему, що й GIL, але їх важко реалізувати.
Як працювати з GIL Python
Використання багатопроцесорності є найбільш прийнятним способом запобігання програмі від GIL. Python пропонує різні інтерпретатори для кожного процесу, тому в цьому сценарії для кожного процесу в багатопроцесорній обробці надається один потік. Давайте розберемося в наступному прикладі.
приклад -
from multiprocessing import Pool import time COUNT = 50000000 def countdown(num): while num>0: num -= 1 if __name__ == '__main__': pool = Pool(processes=2) start_time = time.time() r1 = pool.apply_async(countdown, [COUNT//2]) r2 = pool.apply_async(countdown, [COUNT//2]) pool.close() pool.join() end_time = time.time() print('Time taken in seconds -', end_time - start_time)
Вихід:
Time taken in seconds - 3.3707828521728516
Може здатися, що продуктивність зросла, але керування процесами має власні накладні витрати, і кілька процесів важчі, ніж кілька потоків.
Висновок
У цьому підручнику ми обговорювали GIL і способи його використання. Це дає керування одному потоку для виконання в певний час. У цьому посібнику також пояснюється, чому GIL важливий для програмістів на Python.