Python es un lenguaje que se ejecuta en un intérprete. Según la documentación, Python tiene un bloqueo global (GIL). Al usar múltiples subprocesos (Thread), no se puede aprovechar la ventaja de múltiples núcleos. Sin embargo, al usar múltiples procesos (Multiprocess), se puede aprovechar la ventaja de múltiples núcleos y mejorar verdaderamente la eficiencia.
Experimento comparativo
La documentación indica que si el proceso con múltiples subprocesos esintensivo en CPU, entonces el multihilo no puede mejorar mucho la eficiencia; al contrario, puede incluso reducir la eficiencia debido al cambio frecuente de subprocesos. Se recomienda usar multiproceso; si esintensivo en E/S, el proceso multihilo puede aprovechar el tiempo de inactividad durante la espera de bloqueo de E/S para ejecutar otros subprocesos, mejorando la eficiencia. Por lo tanto, comparamos la eficiencia en diferentes escenarios mediante experimentos.
| Sistema operativo | CPU | Memoria | Disco duro |
|---|---|---|---|
| Windows 10 | Doble núcleo | 8GB | Disco duro mecánico |
(1) Importar los módulos necesarios
import requests import time from threading import Thread from multiprocessing import Process
(2) Definir funciones de cálculo intensivo de CPU
def count(x, y):
# 使程序完成50万计算
c = 0
while c < 500000:
c += 1
x += x
y += y
(3) Definir funciones de lectura/escritura de archivos intensivas en E/S
def write():
f = open("test.txt", "w")
for x in range(5000000):
f.write("testwrite\n")
f.close()
def read():
f = open("test.txt", "r")
lines = f.readlines()
f.close()
(4) Definir funciones de solicitudes de red
_head = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36'}
url = "http://www.tieba.com"
def http_request():
try:
webPage = requests.get(url, headers=_head)
html = webPage.text
return {"context": html}
except Exception as e:
return {"error": e}
(5) Probar el tiempo requerido para la ejecución lineal de operaciones intensivas en E/S, operaciones intensivas en CPU y operaciones intensivas en solicitudes de red
# CPU密集操作
t = time.time()
for x in range(10):
count(1, 1)
print("Line cpu", time.time() - t)
# IO密集操作
t = time.time()
for x in range(10):
write()
read()
print("Line IO", time.time() - t)
# 网络请求密集型操作
t = time.time()
for x in range(10):
http_request()
print("Line Http Request", time.time() - t)
Salida
Intensivo en CPU: 95.6059999466, 91.57099986076355, 92.52800011634827, 99.96799993515015
Intensivo en E/S: 24.25, 21.76699995994568, 21.769999980926514, 22.060999870300293
Intensivo en solicitudes de red: 4.519999980926514, 8.563999891281128, 4.371000051498413, 4.522000074386597, 14.671000003814697
(6) Probar el tiempo requerido para la ejecución concurrente de operaciones intensivas en CPU con múltiples subprocesos (multihilo)
counts = []
t = time.time()
for x in range(10):
thread = Thread(target=count, args=(1,1))
counts.append(thread)
thread.start()
e = counts.__len__()
while True:
for th in counts:
if not th.is_alive():
e -= 1
if e <= 0:
break
print(time.time() - t)
Output: 25.69700002670288、24.02400016784668
(7) Probar el tiempo requerido para la ejecución concurrente de operaciones intensivas en E/S con múltiples subprocesos (multihilo)
def io():
write()
read()
t = time.time()
ios = []
t = time.time()
for x in range(10):
thread = Thread(target=count, args=(1,1))
ios.append(thread)
thread.start()
e = ios.__len__()
while True:
for th in ios:
if not th.is_alive():
e -= 1
if e <= 0:
break
print(time.time() - t)
Output: 99.9240000248 、101.26400017738342、102.32200002670288
(8) Probar el tiempo requerido para la ejecución concurrente de operaciones intensivas en red con múltiples subprocesos (multihilo)
t = time.time()
ios = []
t = time.time()
for x in range(10):
thread = Thread(target=http_request)
ios.append(thread)
thread.start()
e = ios.__len__()
while True:
for th in ios:
if not th.is_alive():
e -= 1
if e <= 0:
break
print("Thread Http Request", time.time() - t)
Output: 0.7419998645782471、0.3839998245239258、0.3900001049041748
(9) Probar el tiempo requerido para la ejecución concurrente de operaciones intensivas en CPU con múltiples procesos (multiproceso)
counts = []
t = time.time()
for x in range(10):
process = Process(target=count, args=(1,1))
counts.append(process)
process.start()
e = counts.__len__()
while True:
for th in counts:
if not th.is_alive():
e -= 1
if e <= 0:
break
print("Multiprocess cpu", time.time() - t)
Output: 54.342000007629395、53.437999963760376
(10) Probar la ejecución concurrente de operaciones intensivas en E/S con múltiples procesos (multiproceso)
t = time.time()
ios = []
t = time.time()
for x in range(10):
process = Process(target=io)
ios.append(process)
process.start()
e = ios.__len__()
while True:
for th in ios:
if not th.is_alive():
e -= 1
if e <= 0:
break
print("Multiprocess IO", time.time() - t)
Output: 12.509000062942505、13.059000015258789
(11) Probar la ejecución concurrente de operaciones intensivas en solicitudes HTTP con múltiples procesos (multiproceso)
t = time.time()
httprs = []
t = time.time()
for x in range(10):
process = Process(target=http_request)
ios.append(process)
process.start()
e = httprs.__len__()
while True:
for th in httprs:
if not th.is_alive():
e -= 1
if e <= 0:
break
print("Multiprocess Http Request", time.time() - t)
Output: 0.5329999923706055、0.4760000705718994
Resultados experimentales
| Operaciones intensivas en CPU | Operaciones intensivas en E/S | Operaciones intensivas en solicitudes de red | |
|---|---|---|---|
| Operación lineal | 94.91824996469 | 22.46199995279 | 7.3296000004 |
| Operación con múltiples subprocesos (multihilo) | 101.1700000762 | 24.8605000973 | 0.5053332647 |
| Operación con múltiples procesos (multiproceso) | 53.8899999857 | 12.7840000391 | 0.5045000315 |
A través de los resultados anteriores, podemos ver que:
El multihilo no parece tener una gran ventaja en operaciones intensivas en E/S (quizás si las tareas de E/S fueran más pesadas, se podría reflejar la ventaja). En operaciones intensivas en CPU, es claramente peor que la ejecución lineal con un solo subproceso. Sin embargo, para operaciones como solicitudes de red que bloquean subprocesos con espera activa, la ventaja del multihilo es muy notable.
El multiproceso muestra ventajas de rendimiento tanto en operaciones intensivas en CPU como en operaciones intensivas en E/S y en operaciones intensivas en solicitudes de red (operaciones donde ocurren bloqueos frecuentes de subprocesos). Sin embargo, en operaciones similares a las intensivas en solicitudes de red, el rendimiento es casi el mismo que el multihilo, pero consume más recursos como CPU. Por lo tanto, en este caso, podemos elegir el multihilo para ejecutar.

Dirección original: http://blog.atomicer.cn/2016/09/30/Python%E4%B8%AD%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%92%8C%E5%A4%9A%E8%BF%9B%E7%A8%8B%E7%9A%84%E5%AF%B9%E6%AF%94/