Введение в GIL и новый GIL
Upcoming SlideShare
Loading in...5
×
 

Введение в GIL и новый GIL

on

  • 539 views

Автор: Андрей Нехайчик (Wargaming.net | COOO «Гейм Стрим») ...

Автор: Андрей Нехайчик (Wargaming.net | COOO «Гейм Стрим»)

— Треды, отличия от потоков.
— Как использовать треды.
— Тестирование производительности (и облом).
— Представление GIL, как он работает.
— Освобождение по I/O, 100 тиков.
— Зачем нужен GIL.
— Проблемы переключения потоков (медленный захват).
— Проблема 100 тиков.
— Проблема отсутствия приоритетов и их типов.
— Новый GIL, 5 миллисекунд, drop_request.
— Когда drop_request не работает.
— Соревнование CPU и I/O тредов.
— Как борются с GIL: тезисы о numpy, Jython, multiprocessing.

Statistics

Views

Total Views
539
Views on SlideShare
539
Embed Views
0

Actions

Likes
2
Downloads
7
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Введение в GIL и новый GIL Введение в GIL и новый GIL Presentation Transcript

  • Введение в потоки GIL и новый GIL Андрей Нехайчик Wargaming.net
  • Из чего сделана эта презентация 3 доклада David Beazley: ● Inside the Python GIL, июнь 2009 ● Inside the New GIL, январь 2010 ● Understanding the Python GIL, февраль 2010 http://www.dabeaz.com/GIL/ - Специфический внутряк для разработчиков ядра питона + Свежие тесты
  • Немного о потоках ● для распараллеливания однотипных задач ● используется pthread Отличия от процессов ● поток – наименьшая единица обработки ● поток – составной элемент процесса ● потоки работают в едином адресном пространстве ● потоки “дешевле” и, обычно, ОС проще ими манипулировать
  • Как работают потоки в идеале 1 ядро
  • Как работают потоки в идеале 3 ядра
  • Пример def count(n): while n > 0: n -= 1 t1 = Thread(target=count, args=(100000000, )) t2 = Thread(target=count, args=(100000000, )) t1.start() t2.start() t1.join() t2.join()
  • Пример def count(n): while n > 0: n -= 1 t1 = Thread(target=count, args=(100000000, )) t2 = Thread(target=count, args=(100000000, )) t1.start() t2.start() t1.join() t2.join() прекратите тиражировать бесполезный пример
  • Пример: поиск простых чисел def is_odd_prime(num): for multiplier in range(3, num, 2): if num % multiplier == 0: return False return True def store_odd_primes(storage, max_number, start_number=3): for num in range(start_number, max_number + 1, 2): if is_odd_prime(num): storage.append(num) numbers = [2] store_odd_primes(numbers, 20000) store_odd_primes(numbers, 40000, start_number=20001) print len(numbers)
  • Пример: поиск простых чисел from threading import Thread ... numbers = [2] thread1=Thread(target=store_odd_primes, args=(numbers, 20000, )) thread1.start() thread2=Thread(target=store_odd_primes, args=(numbers, 40000, ), kwargs={'start_number': 20001}) thread2.start() thread1.join() thread2.join() print len(numbers)
  • Результаты Поиск простых чисел до 40000 неоптимальным алгоритмом Окружение Последовательное выполнение 2 потока (1 ядро) 2 потока (2 ядра) python 2.7.5 (2GHz, Gentoo) 5.40 5.41 5.92 (+10%) python 3.2.5 8.80 9.02 12.11 (+40%) python 3.3.3 8.90 8.90 12.20 (+40%) python 2.7.3 (1GHz, iOS 7, iPad 3) 23.90 - 25.30 (+5%) python 2.7.2 (2.5GHz, OS X 10.8) 3.56 - 3.90 (+10%)
  • Потоки в Python ● Существует GIL и гарантирует последовательное выполнение байткода ● Каждые 100 тиков: освобождение и захват GIL ● 1 тик – одна или более инструкций байткода ● IO освобождает GIL ● Си-модули могут освобождать GIL ● Освобождение и захват GIL – дополнительные накладные расходы
  • Потоки в Python
  • Обработка сигналов
  • Профилирование однопоточной программы python -m cProfile prime_seq.py ncalls 1 1 tottime 0.001 0.015 9999 1229 0.797 0.001 1 0.000 # 10000 filename:lineno(function) prime_seq.py:1(<module>) prime_seq.py:3 (get_prime_list) prime_seq.py:5(is_prime) {method 'append' of 'list' objects} {method 'disable' of '_lsprof.Profiler' objects}
  • Профилирование многопоточной программы ncalls tottime percall cumtime percall filename:lineno(function) ……… 2 0.000 0.000 0.886 0.443 threading.py:909(join) 25 0.886 0.035 0.886 0.035 {method 'acquire' of 'thread.lock'} 97 0.000 0.000 0.000 0.000 {method 'append' of 'list'} 1 0.000 0.000 0.000 0.000 {method 'disable' of ……… '_lsprof.Profiler'} 2 0.000 0.000 0.000 0.000 {method 'extend' of 'list'} 2 0.000 0.000 0.000 0.000 {method 'get' of 'dict'} 1 0.000 0.000 0.000 0.000 {method 'insert' of 'list'} 2 0.000 0.000 0.000 0.000 {method 'items' of 'dict'} 1 0.000 0.000 0.000 0.000 {method 'lower' of 'str'} 12 0.000 0.000 0.000 0.000 {method 'release' of 'thread.lock'} 1 0.000 0.000 0.000 0.000 {method 'rfind' of 'str'} 2 0.000 0.000 0.000 0.000 {method 'setter' of 'property'} 6 0.000 0.000 0.000 0.000 {method 'write' of 'file'}
  • Вернёмся к начальному примеру def count(n): while n > 0: n -= 1 Последовательное 2 потока (2 ядра) Разница python 2.7.5, Gentoo, 2Ghz 20.0 27.7 x1.4 python 3.2.5 19.7 34.5 x1.8 David Beazley OS X, 2GHz 24.6 45.5 x1.8 python 2.7.2 OS X 10.8, 2.5 Ghz i5 15.2 23.7 x1.6
  • Почему такие большие накладные расходы (1 ядро)?
  • Почему такие большие накладные расходы (2 ядра)?
  • Визуализация попыток захвата
  • Промежуточные итоги ● Код параллельно не выполняется ● Но IO (всегда) и CPython расширения (некоторые) освобождают GIL ● Нет планировщика потоков ● Сигналы обрабатываются в главном потоке ● Потоки с интенсивным использованием CPU передерживают GIL ● Провальные попытки захвата GIL
  • Зачем нужен GIL? ● Защита операций работы с памятью в ядре ● Упрощение кода ● Скорость выше, если код проще
  • GIL в python 3.2 ● Первое серьёзное изменение со времён 1992 года ● Вместо счётчика тиков - gil_drop_request ● Также добавлен таймаут в 5мс
  • Как это работает?
  • Проблемы с таймаутом
  • Проблемы из-за отсутствия планировщика
  • Замедление IO
  • Какие планы по GIL Его заменят, если новый подход будет: ● простым ● увеличит скорость для многопоточных программ ● не изменит скорость для однопоточных ● будет совместим с текущим API ядра ● оставит такое же поведение GC
  • Как обойти GIL ● Для IO этого делать не надо ● Использовать специализированные библиотеки: https://wiki.python.org/moin/ParallelProcessing (например scipy) ● Использовать другие интерпретаторы ● Использовать multiprocessing
  • Использовать другие интерпретаторы Последовательное 2 потока (2 ядра) 4 потока (4 ядра) jython 2.5.3 (Gentoo, i3 3Ghz) 10.70 8.65 (x1.24) 6.85 (x1.56) pypy 2.0.2 (Gentoo, i3 3Ghz) 2.75 2.85 2.90
  • Multiprocessing from multiprocessing import Process, Pipe … def store_odd_primes(pipe_conn, max_number, start_number=3): storage = [] for num in range(start_number, max_number + 1, 2): if is_odd_prime(num): storage.append(num) pipe_conn.send(storage) numbers = [2] parent_conn, child_conn = Pipe() proc1 = Process(target=store_odd_primes, args=(child_conn, 20000, )) proc2 = Process(target=store_odd_primes, args=(child_conn, 40000, ), kwargs={'start_number': 20001}) proc1.start();proc2.start();proc1.join();proc2.join() numbers += parent_conn.recv() numbers += parent_conn.recv() print len(numbers)
  • Multiprocessing Последовательное 2 процесса (2 ядра) 4 потока (4 ядра) python 2.6.8 (Gentoo, i3 3Ghz) 3.80 2.80 (x1.35) 2.08 (x1.83) python 3.2.5 (Gentoo, i3 3Ghz) 5.05 3.68 (x1.37) 3.10 (x1.63) pypy 2.0.2 (Gentoo, i3 3Ghz) 0.50 0.42 (x1.20) 0.33 (x1.55)
  • Выводы ● Параллельной многопоточности в Python по умолчанию нет ● Она частично существует для специализированных расширений и операций IO ● GIL почти никогда не мешает ● А когда мешает мы знаем как с этим бороться
  • Спасибо.