2 Глобальная блокировка интерпретатора

Python производительность многопоточной часто страдает из - за глобальным интерпретатор Замка . Короче говоря, даже если вы можете иметь несколько потоков в программе Python, только одна инструкция байт-кода может выполняться параллельно в любое время, независимо от количества процессоров.

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

 import threading
import time


def process():
    time.sleep(2)


start = time.time()
process()
print("One run took %.2fs" % (time.time() - start))


start = time.time()
threads = [threading.Thread(target=process) for _ in range(4)]
for t in threads:
    t.start()
for t in threads:
    t.join()
print("Four runs took %.2fs" % (time.time() - start))

# Out: One run took 2.00s
# Out: Four runs took 2.00s 

Обратите внимание , что даже если каждый process занимает 2 секунды , чтобы выполнить эти четыре процесса вместе были в состоянии эффективно работать параллельно, принимая всего 2 секунды.

Тем не менее, многопоточность в тех случаях, когда в коде Python выполняются интенсивные вычисления, такие как много вычислений, не приводит к значительным улучшениям и даже может быть медленнее, чем параллельная работа:

 import threading
import time


def somefunc(i):
    return i * i

def otherfunc(m, i):
    return m + i

def process():
    for j in range(100):
        result = 0
        for i in range(100000):
            result = otherfunc(result, somefunc(i))


start = time.time()
process()
print("One run took %.2fs" % (time.time() - start))


start = time.time()
threads = [threading.Thread(target=process) for _ in range(4)]
for t in threads:
    t.start()
for t in threads:
    t.join()
print("Four runs took %.2fs" % (time.time() - start))

# Out: One run took 2.05s
# Out: Four runs took 14.42s 

В последнем случае многопроцессорная обработка может быть эффективной, поскольку несколько процессов могут, конечно, выполнять несколько инструкций одновременно:

 import multiprocessing
import time


def somefunc(i):
    return i * i

def otherfunc(m, i):
    return m + i

def process():
    for j in range(100):
        result = 0
        for i in range(100000):
            result = otherfunc(result, somefunc(i))


start = time.time()
process()
print("One run took %.2fs" % (time.time() - start))


start = time.time()
processes = [multiprocessing.Process(target=process) for _ in range(4)]
for p in processes:
    p.start()
for p in processes:
    p.join()
print("Four runs took %.2fs" % (time.time() - start))

# Out: One run took 2.07s
# Out: Four runs took 2.30s