Threading в Python занимает больше времени, а не ускоряет его?

Я написал 3 разных кода, чтобы сравнить наличие потоков и отсутствие потоков. В основном измерение того, сколько времени я экономлю, используя многопоточность, и результат не имеет никакого смысла.

Вот мои коды:

 import time



def Function():

    global x 
    x = 0

    while x < 300000000:
        x += 1
    print x

e1 = time.clock()
E1 = time.time()

Function() 

e2 = time.clock()
E2 = time.time()

print e2 - e1
print E2 - E1 

Когда я запустил это, я получил это как вывод:

26.6358742929

26.6440000534

Затем я написал еще одну функцию, как показано ниже, и разделил счет до 300 миллионов на счет 3, 100 миллионов:

 import time




def Function():

    global x 
    x = 0

    while x < 100000000:
        x += 1
    print x

def Function2():

    global x 
    x = 0

    while x < 100000000:
        x += 1
    print x       


def Function3():

    global x 
    x = 0

    while x < 100000000:
        x += 1
    print x 

e1 = time.clock()
E1 = time.time()

Function() 
Function2() 
Function3() 

e2 = time.clock()
E2 = time.time()

print e2 - e1
print E2 - E1   

Вывод следующей функции:

26.0577638729

26.0629999638

и, наконец, я создал 3 потока и запустил каждую функцию в одном потоке:

import time
import threading

e1 = time.clock()
E1 = time.time()

def Function1():

    global x 
    x = 0

    while  x < 100000000:
        x += 1
    print x


def Function2():

    global x 
    x = 0

    while x < 100000000:
        x += 1
    print x    


def Function3():

    global x 
    x = 0

    while x < 100000000:
        x += 1
    print x    



new_thread1  = threading.Thread(target = Function1() , args = ())

new_thread2  = threading.Thread(target = Function2(), args = ())

new_thread3  = threading.Thread(target = Function3(), args = ())


e1 = time.clock()
E1 = time.time()

new_thread1.start()
new_thread2.start()
new_thread3.start()

e2 = time.clock()
E2 = time.time()

print e2 - e1
print E2 - E1 

Результатом этого было:

0.000601416222253

0.0

Эти цифры не имеют для меня никакого смысла. Я просто пытаюсь измерить, сколько времени экономит мне многопоточность. Я просмотрел документацию, и использование time.time и time.clock имело для меня смысл, но здесь это не имеет смысла. Кроме того, фактическое время для 1-го и 2-го фрагмента было около 10 секунд, а для 3-го - около 5.


person OpenCv    schedule 08.04.2015    source источник
comment
или, может быть, он вызывает функции, когда создает потоки... эти тайминги вообще ничего не значат   -  person Joran Beasley    schedule 08.04.2015
comment
Многопоточность позволяет выполнять другую работу, пока один поток заблокирован при вводе-выводе. Все ваши функции связаны с вычислениями; они ограничены только тем, сколько процессорного времени они могут получить, и с одним процессом время, затрачиваемое на управление потоками, — это просто время, отнятое от реальных вычислений.   -  person chepner    schedule 08.04.2015
comment
@chepner как я могу сделать это быстрее?   -  person OpenCv    schedule 08.04.2015
comment
Вам нужно использовать несколько процессов, чтобы каждая функция выполнялась в процессе на отдельном процессоре.   -  person chepner    schedule 08.04.2015
comment
@chepner, поэтому я должен использовать многопроцессорность вместо многопоточности ?! Я предполагал, что потоки будут работать на этом. Если у меня есть работа, не связанная с вычислениями, ускорит ли потоки мой код? Извините за множественный вопрос. Новичок в программировании здесь.   -  person OpenCv    schedule 08.04.2015
comment
Вы ошиблись :) Примером использования многопоточности могут быть два потока, которые время от времени обращаются к диску. Пока один поток заблокирован в ожидании завершения чтения (или записи) диска, вы можете переключиться на другой поток, который может выполнять работу, не связанную с вводом-выводом.   -  person chepner    schedule 08.04.2015
comment
@OpenCv В большинстве языков программирования потоки будут делать то, что вы ожидаете здесь. Однако у Python есть конструктивное ограничение (дополнительную информацию см. в разделе Глобальная блокировка интерпретатора), которое не позволяет одновременно выполнять операции ЦП более чем одному потоку, даже если у вас есть несколько ядер ЦП для работы. Это означает, что использование нескольких потоков для работы, связанной с ЦП, не повышает производительность. В Python потоки могут повысить производительность только в работе, связанной с вводом-выводом.   -  person dano    schedule 08.04.2015
comment
Потоки также могут помочь в ситуациях, когда прогресс важнее, чем завершение. Например, для 10 пользователей, пытающихся загрузить веб-страницу, которая может быть передана за 10 секунд, может быть лучше, если каждый получит 10% файла через 10 секунд, чем если бы один пользователь получил 100% файла, а остальные 9 все еще жду начала. (Что на самом деле является просто формой работы, связанной с вводом-выводом, если пользователь не может прочитать 10% на своем экране до того, как сервер начнет отправлять больше данных.)   -  person chepner    schedule 08.04.2015
comment
@dano спасибо за объяснение. Я просто пытаюсь действительно понять это. В конечном итоге я пытаюсь использовать многопоточность для обработки изображений, и в документации говорится, что программы обработки изображений выполняются вне GIL. вот что я прочитал: обратите внимание, что потенциально блокирующие или длительные операции, такие как ввод-вывод, обработка изображений и обработка чисел NumPy, происходят вне GIL. Я просто пытался проверить время в приведенном выше коде. Итак, если я использую функции, которые у меня есть для обработки изображений с использованием потоков, у меня не будет проблем?   -  person OpenCv    schedule 08.04.2015
comment
@OpenCv Все, что работает за пределами GIL, должно работать параллельно между процессорами. И да, это правда, что есть определенные библиотеки, использующие C-расширения, которые могут выполнять обработку с привязкой к ЦП вне GIL. Поэтому, если ваша библиотека обработки изображений выпускает GIL для обработки, она может работать в нескольких потоках параллельно между процессорами. Все остальное, что происходит в потоках, потребует GIL и не будет выполняться параллельно. Если подавляющее большинство работы, выполняемой потоком, высвобождает GIL, вы должны увидеть повышение производительности без необходимости использования multiprocessing.   -  person dano    schedule 08.04.2015
comment
@OpenCv отредактировал мой ответ, чтобы более точно обратиться к вашему примеру кода.   -  person Joran Beasley    schedule 09.04.2015


Ответы (1)


ты неправильно называешь....

 new_thread1  = threading.Thread(target = Function1 , args = ())

обратите внимание, что вы не должны ВЫЗЫВАТЬ функцию при создании потока

эти тайминги действительно ничего не значат, они оба по существу равны нулю, потому что все, что вы синхронизируете, - это 3 мгновенных вызова функции возврата для запуска

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

РЕДАКТИРОВАТЬ ДЛЯ ДОПОЛНИТЕЛЬНОЙ ИНФОРМАЦИИ

с потоковой передачей вы заблокированы gil для одной инструкции python за раз... обычно это не проблема, поскольку вы обычно ждете на диске io... Однако в вашем примере кода это 100% вычисление, поэтому многопоточность действительно не улучшается ваше время ... Многопроцессорность может, как показано ниже

import time
import threading
import multiprocessing

def fn():
    '''since all 3 functions were identical you can just use one ...'''
    x = 0
    while  x < 100000000:
        x += 1
    



def TEST_THREADS():
    new_thread1  = threading.Thread(target = fn , args = ())
    new_thread2  = threading.Thread(target = fn, args = ())
    new_thread3  = threading.Thread(target = fn, args = ())
    new_thread1.start()
    new_thread2.start()
    new_thread3.start()
    new_thread1.join()
    new_thread2.join()
    new_thread3.join()

def TEST_NORMAL():
    fn()
    fn()
    fn()
    
def TEST_MULTIPROCESSING():
    new_thread1  = multiprocessing.Process(target = fn , args = ())
    new_thread2  = multiprocessing.Process(target = fn, args = ())
    new_thread3  = multiprocessing.Process(target = fn, args = ())
    new_thread1.start()
    new_thread2.start()
    new_thread3.start()
    new_thread1.join()
    new_thread2.join()
    new_thread3.join
if __name__ == "__main__":  
    '''It is very important to use name == __main__ guard code with threads and multiprocessing'''
    import timeit
    print "Time to Run 1x: %0.2fs"%(timeit.timeit(fn,number=1),)
    print "NORMAL:%0.2fs"%(timeit.timeit(TEST_NORMAL,number=1),)
    print "Threaded: %0.2fs"%(timeit.timeit(TEST_THREADS,number=1),)
    print "Multiprocessing: %0.2fs"%(timeit.timeit(TEST_MULTIPROCESSING,number=1),)

Я получаю следующий вывод

Time to Run 1x: 3.71181102665
NORMAL:11.0136830117
Threaded: 23.392143814
Multiprocessing: 3.80878260515
person Joran Beasley    schedule 08.04.2015
comment
@joran_beasley, все остальное кажется правильным?! теперь я получаю 0,0523433269691 0,0499999523163, и код работает значительно быстрее. но с другой стороны 1-й и 2-й код не занял 26 секунд - person OpenCv; 08.04.2015
comment
нет, у вас куча проблем... запустите свой таймер в первой строке вашего кода... и остановите его после того, как все ваши потоки воссоединятся (используя threadX.join(), который будет ждать завершения потока) - person Joran Beasley; 08.04.2015
comment
@joran_beasley как в new_thread1.start() new_thread1.join() new_thread2.start() new_thread2.join() new_thread3.start() - person OpenCv; 08.04.2015
comment
не запускать все потоки... а затем присоединиться к ним всем... объединение заблокирует программу до тех пор, пока поток не вернется - person Joran Beasley; 08.04.2015
comment
@joran_beasley спасибо за помощь. это будет делать? pastebin.com/FW1Rqv05 - person OpenCv; 08.04.2015
comment
@joran_beasley время по-прежнему не имеет смысла: первый код занимает около 26 секунд, второй код занимает 40, а третий около 40 секунд. Это полная противоположность тому, что я хочу, чтобы потоки делали... разве потоки не должны ускорять процесс? - person OpenCv; 08.04.2015