程式池、執行緒池效率測試

dwzb發表於2018-03-13

本文首發於知乎
之前我們分別對計算密集型和IO密集型任務,測試過多執行緒對執行效率的改進,下面我們依然分計算密集、檔案讀寫、網路請求三個部分,測試使用執行緒池、程式池如何改進執行效率

首先匯入庫並定義三種任務的函式

import requests
from bs4 import BeautifulSoup
import time
import numpy as np
from multiprocessing.dummy import Pool as ThreadPool
from multiprocessing import Pool
# 計算從1加到50000000
def cal(a = None): # 引數i沒用,只是為了和後面統一
s = 0
for i in range(50000000):
s = s + i
# 5000000次寫入檔案
def file(a = None): # 引數i沒用,只是為了和後面統一
with open('try.txt', 'w') as f:
for i in range(5000000):
f.write('abcd\n')
# 抓取豆瓣top250的10個網頁
def gettitle(a):
url = 'https://movie.douban.com/top250?start={}&filter='.format(a*25)
r = requests.get(url)
soup = BeautifulSoup(r.content, 'html.parser')
lis = soup.find('ol', class_='grid_view').find_all('li')
for li in lis:
title = li.find('span', class_="title").text
print(title)
複製程式碼

下面定義線性計算、多執行緒、多程式的函式

# 分別將上面三個函式傳入,計算10次,返回正常迴圈的執行總時間
def nothing(func):
t = time.time()
for i in range(10):
func(i)
duration = time.time() - t
return duration
# 分別將上面三個函式傳入,計算10次,返回使用多執行緒的執行總時間
def thread(func):
t = time.time()
pool = ThreadPool(4)
pool.map(func, range(10))
duration = time.time() - t
return duration
# # 分別將上面三個函式傳入,計算10次,返回使用多程式的執行總時間
def process(func):
t = time.time()
pool = Pool(4)
pool.map(func, range(10))
duration = time.time() - t
return duration
複製程式碼

下面定義計算執行時間的函式

def get_duration(curr, func):
l = []
for _ in range(5):
l.append(curr(func))
mean_duration = '%.2f' % np.mean(l)
all_duration = ['%.2f' % i for i in l]
return mean_duration, all_duration
複製程式碼

下面執行程式碼計算時間

if __name__ == '__main__':
# CPU密集任務對比
print(get_duration(nothing, cal))
print(get_duration(thread, cal))
print(get_duration(process, cal))
# 檔案讀寫任務對比
print(get_duration(nothing, file))
print(get_duration(thread, file))
print(get_duration(process, file))
# 網路請求任務對比
print(get_duration(nothing, gettitle))
print(get_duration(thread, gettitle))
print(get_duration(process, gettitle))
複製程式碼

結果如下

------CPU密集型任務執行時間-------
線性運算
('39.98', ['39.57', '39.36', '40.53', '40.09', '40.35'])
多執行緒
('38.31', ['39.07', '37.96', '38.07', '38.31', '38.13'])
多程式
('27.43', ['27.58', '27.11', '27.82', '27.53', '27.11'])
------檔案讀寫任務執行時間-------
線性運算
('54.11', ['53.54', '53.96', '54.46', '53.54', '55.03'])
多執行緒
('53.86', ['55.44', '54.12', '52.48', '53.17', '54.08'])
多程式
('34.98', ['35.14', '34.35', '35.27', '35.20', '34.94'])
------網路請求任務執行時間-------
線性運算
('4.77', ['4.74', '4.70', '4.77', '4.91', '4.72'])
多執行緒
('1.96', ['1.88', '2.09', '1.91', '2.04', '1.91'])
多程式
('3.79', ['3.55', '3.70', '3.50', '3.92', '4.30'])
複製程式碼

分析如下

  • 首先,CPU密集型運算。多執行緒無法改善執行效率,多程式可以改善。因為多程式能夠利用計算機的多核優勢,呼叫了更多資源進行計算

  • 在進行CPU密集運算時,可以監測工作管理員,發現線上性運算和多執行緒時,CPU利用率連一半都不到;而在多程式時就跑滿了所有的CPU

  • 注意一點:這裡使用執行緒池(程式池)都只開了4個執行緒(程式),因為在我的計算機上,用4個程式可以最大化利用CPU的計算能力,開更多程式也無法在計算密集型任務執行上有更大的優勢,反而會增加程式建立和切換的時間。

  • 其次,檔案讀寫任務。上面結果顯示只有多程式對執行效率有所改善。

  • 其實檔案讀寫任務有時候多執行緒也是可以改善效率的,是在開啟檔案、讀寫檔案比較慢的時候,而上面展示的可能因為檔案比較小,讀入內容比較少,所以耗費的時間基本在於操作頻繁,還是CPU負載問題,所以多執行緒無法提高執行效率

  • 如果讀寫檔案是與資料庫連線,等待的時間就會長一些,這時多執行緒也能發揮更大的優勢

  • 為了更好地展現多執行緒在檔案讀寫方面的優勢,我又進行了下面測試

file函式改為(即每次寫入內容增多)

# 500次寫入檔案
def file(a = None): # 引數i沒用,只是為了和後面統一
for i in range(500):
with open('try.txt', 'w') as f:
f.write('abcd'*100000 + '\n')
複製程式碼

執行結果如下

線性運算
('55.15', ['49.96', '55.75', '45.11', '52.16', '72.75'])
多執行緒
('26.57', ['26.67', '23.89', '25.48', '32.84', '23.94'])
多程式
('25.72', ['24.10', '25.82', '24.13', '28.03', '26.50'])
複製程式碼

可以看出這種情況下,多執行緒對效率的改善程度和多程式差不多。

  • 再次,網路請求任務。上面結果顯示多執行緒對這種任務的效率改善最為明顯,多程式也有些許改善。

  • 因為網路請求任務最主要的時間消耗在於等待網頁的回覆,這時如果能同時等待多個網頁的回覆,就能極大提高執行效率,多執行緒在此可以完美髮揮作用。

  • 最後注意一點,使用多程式時,建立程式時間開銷非常大,所以上面程式碼只有通過提高函式執行時間來、才能展示多程式的優勢。網路請求任務多程式沒有改善很多的原因也正在於此。而建立多執行緒則輕鬆很多。

  • 結論:CPU密集任務一般用多程式提高執行效率,網路請求任務一般用多執行緒提高執行效率,檔案讀寫看主要是CPU計算耗時還是等待耗時。

歡迎關注我的知乎專欄

專欄主頁:python程式設計

專欄目錄:目錄

版本說明:軟體及包版本說明

相關文章