Параллелизм в python работает неправильно

Я разрабатывал приложение на gae с использованием python 2.7, вызов ajax запрашивает некоторые данные из API, один запрос может занять ~ 200 мс, однако, когда я открываю два браузера и делаю два запроса в очень близкое время, они занимают больше, чем вдвое больше, я пытался поместить все в потоки, но это не сработало .. (это происходит, когда приложение находится в сети, а не только на сервере разработки)

Поэтому я написал этот простой тест, чтобы увидеть, является ли это проблемой в python вообще (на случай ожидания), вот код и результат:

def work():
    t = datetime.now()
    print threading.currentThread(), t
    i = 0
    while i < 100000000:
        i+=1
    t2 = datetime.now()
    print threading.currentThread(), t2, t2-t

if __name__ == '__main__': 
    print "single threaded:"
    t1 = threading.Thread(target=work)
    t1.start()
    t1.join()

    print "multi threaded:"
    t1 = threading.Thread(target=work)
    t1.start()
    t2 = threading.Thread(target=work)
    t2.start()
    t1.join()
    t2.join()

Результат на mac os x, core i7 (4 ядра, 8 потоков), python2.7:

single threaded:
<Thread(Thread-1, started 4315942912)> 2011-12-06 15:38:07.763146
<Thread(Thread-1, started 4315942912)> 2011-12-06 15:38:13.091614 0:00:05.328468

multi threaded:
<Thread(Thread-2, started 4315942912)> 2011-12-06 15:38:13.091952
<Thread(Thread-3, started 4323282944)> 2011-12-06 15:38:13.102250
<Thread(Thread-3, started 4323282944)> 2011-12-06 15:38:29.221050 0:00:16.118800
<Thread(Thread-2, started 4315942912)> 2011-12-06 15:38:29.237512 0:00:16.145560

Это довольно шокирует!! если одному потоку потребуется 5 секунд, чтобы сделать это. Я думал, что запуск двух потоков одновременно займет одно и то же время, чтобы завершить обе задачи, но это занимает почти в три раза больше времени.. это делает всю идею потоковой передачи бесполезной, поскольку было бы быстрее делать их последовательно!

чего мне тут не хватает..


person Mohamed Khamis    schedule 06.12.2011    source источник
comment
Вы читали что-нибудь о глобальной блокировке интерпретатора (GIL) в Python? Если вам нужна параллельная обработка, вам следует обратить внимание на многопроцессорность, а не на многопоточность. Выполнение ограничено одним потоком за раз, если только библиотеки, с которыми вы работаете, не предназначены специально для выпуска GIL.   -  person g.d.d.c    schedule 06.12.2011
comment
Ваш тест плохо разработан. Ваш фактический вариант использования будет связан с вводом-выводом, а не с привязкой к процессору. В каждом случае Python GIL ведет себя совершенно по-разному. Многопоточность должна нормально работать в вашем реальном случае использования.   -  person zeekay    schedule 06.12.2011
comment
@g.d.d.c. многопроцессорность недоступна в GAE   -  person bpgergo    schedule 06.12.2011
comment
@bpgergo Это среда выполнения Python 2.7.   -  person Nick Johnson    schedule 07.12.2011
comment
Другая проблема, которую вы видите, Мохамед, заключается в том, что dev_appserver App Engine является однопоточным, поэтому несколько запросов выполняются последовательно. В производстве такого нет. Не ожидайте, что показатели производительности на сервере dev_appserver будут репрезентативными для производственного поведения.   -  person Nick Johnson    schedule 07.12.2011


Ответы (3)


Дэвид Бизли выступил с докладом об этой проблеме на PyCon 2010 , Как уже заявляли другие, для некоторых задач использование многопоточности, особенно с несколькими ядрами, может привести к более низкой производительности, чем та же задача, выполняемая одним потоком. Проблема, как обнаружил Бизли, была связана с "битвой GIL" нескольких ядер:

введите здесь описание изображения

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

import multiprocessing as mp
import datetime as dt
def work():
    t = dt.datetime.now()
    print mp.current_process().name, t
    i = 0
    while i < 100000000:
        i+=1
    t2 = dt.datetime.now()
    print mp.current_process().name, t2, t2-t

if __name__ == '__main__': 
    print "single process:"
    t1 = mp.Process(target=work)
    t1.start()
    t1.join()

    print "multi process:"
    t1 = mp.Process(target=work)
    t1.start()
    t2 = mp.Process(target=work)
    t2.start()
    t1.join()
    t2.join()

урожаи

single process:
Process-1 2011-12-06 12:34:20.611526
Process-1 2011-12-06 12:34:28.494831 0:00:07.883305
multi process:
Process-3 2011-12-06 12:34:28.497895
Process-2 2011-12-06 12:34:28.503433
Process-2 2011-12-06 12:34:36.458354 0:00:07.954921
Process-3 2011-12-06 12:34:36.546656 0:00:08.048761

PS. Как отметил zeekay в комментариях, битва с GIL серьезна только для задач, связанных с процессором. Это не должно быть проблемой для задач, связанных с вводом-выводом.

person unutbu    schedule 06.12.2011
comment
Это поведение характерно для Python? И если да, то почему? - person Paul Draper; 21.04.2013
comment
GIL специфичен для Python, поэтому сражения GIL специфичны для GIL. Я не уверен, что что-то подобное может произойти в других языках. - person unutbu; 21.04.2013

интерпретатор CPython не позволит запускать более одного потока. прочитать о GIL http://wiki.python.org/moin/GlobalInterpreterLock

Таким образом, некоторые задачи не могут выполняться одновременно эффективным способом в CPython с потоками.

Если вы хотите делать что-то параллельно в GAE, то запускайте их параллельно с отдельными запросами.

Кроме того, вы можете обратиться к параллельной вики Python http://wiki.python.org/moin/ParallelProcessing

person bpgergo    schedule 06.12.2011

Я бы посмотрел, куда идет время. Предположим, например, что сервер может отвечать только на один запрос каждые 200 мс. Тогда вы ничего не сможете сделать, вы будете получать только один ответ каждые 200 мс, потому что это все, что может вам предоставить сервер.

person David Schwartz    schedule 06.12.2011
comment
Посмотрите на его код. Он звонит datetime, связи с сервером нет. - person Paul Draper; 21.04.2013