#python
Есть обычная функция которую я пробовал запускать в потоках и без, в каждом по три раза:
import time
def foo(count, num):
for n in range(count):
pass
print('done %s' % num)
start = time.time()
foo(100000000, 1)
foo(100000000, 2)
foo(100000000, 3)
print('Done at:', time.time() - start)
Время исполнения 6.2 сек, теперь запускаю ту же функцию в потоках:
import threading, time
def foo(count, num):
for n in range(count):
pass
print('done %s' % num)
start = time.time()
threads = []
for n in range(3):
t = threading.Thread(target=foo, args=(100000000, n))
t.start()
threads.append(t)
for thread in threads:
thread.join()
print('Done at:', time.time() - start)
Время исполнения 7 сек., теперь запускаю с помощью _thread:
import time, _thread as thread
def foo(count, num):
for n in range(count):
pass
print('done %s' % num)
blocks[num] = True
start = time.time()
blocks = [False for n in range(3)]
for i in range(3):
thread.start_new_thread(foo, (100000000, i))
while not all(blocks):
pass
print('Done at:', time.time() - start)
Здесь вообще почти 10 сек. Почему так ? зачем нужны тогда вообще потоки ели есть
эта глобальная блокировка интерпретатора?
Ответы
Ответ 1
Почему так медленно? Только один поток может исполнять Питон-код в любое время из-за GIL (CPython). Поэтому использование нескольких потоков не ускоряет код, подобный приведённому в вопросе, в Питоне. Пример с _thread у вас ещё медленнее из-за while True цикла в основном потоке. Зачем тогда потоки? Потоки могут быть использованы не только для улучшения производительности, но для реализации асинхронных вызовов, к примеру, чтобы избежать эффект "подвисания" GUI при выполнении блокирующих вызовов (таких как чтение вывода внешних команд): Мультизадачность на Python: выполнить две долгие функции одновременно, не блокируя GUI. GIL отпускается во время блокирующих системных вызовов. В частности, тех которые ввод/вывод реализуют — к примеру, это позволяет скачивать несколько веб-страниц одновременно в Питоне. A very simple multithreading parallel URL fetching (without queue). В тех случаях когда производительность важна, в реальных задачах такие циклы реализованы в C расширениях типа numpy, regex, lxml, sqlite3, или написанных руками на Cython, которые могут отпустить GIL, что может улучшить производительность как на одном ядре на порядок/два, так и позволяет несколько CPU ядер задействовать одновременно. Вызов С функций через ctypes, также отпускает GIL. Реализации Питона, в которых нет GIL существуют уже давно (Jython, IronPython). Очевидно, что они не вытеснили реализации, использующие GIL, такие как CPython, Pypy. Стоит явно упомянуть, что разные Питон-процессы, в частности, такие как созданные с помощью multiprocessing модуля, не разделяют GIL (у каждого процесса свой GIL). Несколько Питон процессов могут все ядра использовать. Модель параллельности (concurrency), реализованная на процессах поощряет разделение данных и использование коммуникации для общения/синхронизации между разными ветками одновременных вычислений, не используя общую память (хотя можно память разделять в некоторых случаях, если необходимо). Это можно суммировать слоганом из Go языка, на который оказала влияние CSP теория Хоара (1978): Do not communicate by sharing memory; instead, share memory by communicating.
Комментариев нет:
Отправить комментарий