Параллелизм в питоне не работает правильно - PullRequest
7 голосов
/ 06 декабря 2011

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

Итак, я написал этопростой тест, чтобы увидеть, если это проблема в 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 секунд, чтобы сделать это .. Я думал, что запуск двух потоков одновременно займет одинаковое время, чтобы завершить обе задачи, но это займет почти втрое время ... это делает всю идею потока бесполезной, так какбыло бы быстрее сделать их последовательно!

что мне здесь не хватает ..

Ответы [ 3 ]

9 голосов
/ 06 декабря 2011

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

enter image description here

Чтобы избежать конфликта GIL, вы можете получить лучшие результаты, если задачи будут выполняться в отдельных процессах, а не в отдельных потоках. Модуль многопроцессорный предоставляет удобный способ сделать это, тем более что многопроцессорный 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 серьезна только для задач, связанных с процессором. Это не должно быть проблемой для задач, связанных с IO.

4 голосов
/ 06 декабря 2011

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

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

Если вы хотите выполнять параллельные операции в GAE, запустите их параллельно с отдельными запросами.

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

1 голос
/ 06 декабря 2011

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

...