欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Python中Sync與Async執(zhí)行速度快慢對比

 更新時間:2023年03月01日 14:45:56   作者:so1n  
Python新的版本中支持了async/await語法, 很多文章都在說這種語法的實現(xiàn)代碼會變得很快, 但是這種快是有場景限制的。這篇文章將嘗試簡單的解釋為何Async的代碼在某些場景比Sync的代碼快

前記

Python新的版本中支持了async/await語法, 很多文章都在說這種語法的實現(xiàn)代碼會變得很快, 但是這種快是有場景限制的。這篇文章將嘗試簡單的解釋為何Async的代碼在某些場景比Sync的代碼快。

1.一個簡單的例子

首先先從一個例子了解兩種調(diào)用方法的差別, 為了能清晰的看出他們的運行時長差別, 都讓他們重復(fù)運行10000次, 具體代碼如下:

import asyncio
import time


n_call = 10000


# sync的調(diào)用時長
def demo(n: int) -> int:
    return n ** n

s_time = time.time()
for i in range(n_call):
    demo(i)
print(time.time() - s_time)

# async的調(diào)用時長
async def sub_demo(n: int) -> int:
    return n ** n

async def async_main() -> None: 
    for i in range(n_call):
        await sub_demo(i)

loop = asyncio.get_event_loop()
s_time = time.time()
loop.run_until_complete(async_main())
print(time.time() - s_time)

# 輸出
# 5.310615682601929
# 5.614157438278198

可以看得出來, sync的語法大家都是很熟悉, 而async的語法比較不一樣, 函數(shù)需要使用async def開頭, 同時調(diào)用async def函數(shù)需要使用await語法, 運行的時候需要先獲取線程的事件循環(huán), 然后在通過事件循環(huán)來運行async_main函數(shù)來達到一樣的效果, 但是從運行結(jié)果的輸出可以看得出, sync的語法在這個場景中比async的語法速度快了一些些(由于Python的GIL原因, 這里無法使用多核的性能, 只能以單核來跑)。

造成這樣的原因是同樣由同一個線程執(zhí)行的情況下(cpu單核心),async的調(diào)用還需要經(jīng)過一些事件循環(huán)的額外調(diào)用, 這會產(chǎn)生一些小開銷, 從而運行時間會比sync的慢, 同時這是一個純cpu運算的示例, 而async的的優(yōu)勢在于網(wǎng)絡(luò)io運算, 在這個場景無法發(fā)揮優(yōu)勢, 但會在高并發(fā)場景則會大放光彩, 造成這樣的原因則是因為async是以協(xié)程運行的, sync是以線程運行的。

NOTE: 目前所說的async語法都是支持網(wǎng)絡(luò)io, 而文件系統(tǒng)的異步io還不是非常的完善, 所以文件系統(tǒng)的異步讀寫是通過封裝交給多線程去處理, 而不是協(xié)程。 具體可見: https://github.com/python/asyncio/wiki/ThirdParty#filesystem

2.一個io的例子

為了了解async在io場景下的運行優(yōu)勢, 先假定有一個io場景--Web后臺服務(wù)通常需要處理許多請求, 所有請求都是從不同的客戶端發(fā)出的, 示例如圖:

在這種場景下, 客戶端請求都是在短時間內(nèi)發(fā)出的。 而服務(wù)端為了能夠在短時間內(nèi)處理大量的請求, 防止處理延遲, 都會以某種方式來支持并發(fā)或者并行。

NOTE: 并發(fā),在操作系統(tǒng)中,是指一個時間段中有幾個程序都處于已啟動運行到運行完畢之間,且這幾個程序都是在同一個處理機上運行,但任一個時刻點上只有一個程序在處理機上運行。 并行是計算機系統(tǒng)中能同時執(zhí)行兩個或多個處理的一種計算方法。

對于sync語法來說, 這個Web后臺可以通過進程, 線程或者兩者結(jié)合來實現(xiàn), 他們的提供并發(fā)/并行的能力會局限于woker的數(shù)量, 比如當有5個客戶端同時請求而服務(wù)端只有4個worker時, 有一個請求會進入阻塞等待階段, 直到運行的4個worker有一個被處理完畢。 為了讓服務(wù)器能提供更好的服務(wù), 我們都會提供足夠多的worker, 同時由于進程具有良好的隔離性且比較每起一個進程都會占用一份獨立的資源, 所以都是以幾個進程+大量線程的形式來提供服務(wù)。

NOTE: 進程是最小的資源分配單位, 過多的進程會占用很多系統(tǒng)資源, 一般的后臺服務(wù)啟用的進程數(shù)量不會很多, 同時線程是最小的調(diào)度單位, 所以以下的調(diào)度我都以線程來描述。

但是這種方式是很耗系統(tǒng)的資源的(相對于協(xié)程來說), 因為線程的運行都是靠cpu來執(zhí)行的, 而cpu是有限的, 同一時刻只能支持固定的幾個worker運行, 其他線程則得等待被調(diào)度, 這樣就意味著每個線程都只能工作一個時間分片, 之后就會被調(diào)度系統(tǒng)控制進入阻塞或者就緒階段, 讓位給其他線程, 直到下次獲取時間分片時才可以繼續(xù)運行。 為了能模擬出同一時刻內(nèi), 多個線程同時運行, 且防止其他線程餓死的情況, 線程每次獲得的運行時間很短, 線程間的調(diào)度切換很頻繁, 當啟用更多的進程和更多的線程時, 調(diào)度就會更加的頻繁。

不過調(diào)度線程的開銷還不算大, 比較大的開銷是調(diào)度線程而產(chǎn)生的下文切換和競爭條件(具體可以參考《計算機導論》中進程調(diào)度相關(guān)的資料, 我這里只是簡單說明), cpu在執(zhí)行代碼時,它需要把數(shù)據(jù)加載到cpu的緩存中去的再運行, 當cpu運行的線程在這個時間分片內(nèi)執(zhí)行完成時, 該線程的最新運行數(shù)據(jù)就會保存起來, 然后cpu會去加載準備被調(diào)度的線程的數(shù)據(jù), 并運行。 雖然這部分暫存數(shù)據(jù)是保存在比內(nèi)存更快, 比內(nèi)存更靠近cpu的寄存器上, 但是寄存器的訪問速度也沒有cpu緩存的訪問速度快, 所以cpu在切換運行的線程時, 都會花上一部分時間用來裝載數(shù)據(jù)上還有裝載緩存時的競爭問題。

對比線程的調(diào)度產(chǎn)生的上下文切換與搶占式, async語法實現(xiàn)的協(xié)程是非搶占式的, 協(xié)程的調(diào)度是依賴于一個循環(huán)來控制, 這個循環(huán)是一個非常常高效的任務(wù)管理器和調(diào)度器, 由于調(diào)度的是一段代碼的實現(xiàn)邏輯, 所以cpu的執(zhí)行代碼并不用切換, 也就沒有上下文切換的開銷, 同時, 也不用考慮裝載緩存的競爭問題。 還是以上面那個圖為例子, 在服務(wù)開始啟動時, 會先啟動一個事件循環(huán), 當收到請求時, 它會創(chuàng)建一個任務(wù)來處理客戶端發(fā)送過來的請求, 這個任務(wù)會從事件循環(huán)獲取到了執(zhí)行權(quán),獨占整個線程資源并一直執(zhí)行, 直到遇到需要等待外部事件, 比如等待數(shù)據(jù)庫返回數(shù)據(jù)的事件, 這時任務(wù)會告訴事件循環(huán)自己在等待這個事件, 然后交出執(zhí)行權(quán), 事件循環(huán)就會把執(zhí)行權(quán)傳遞給最需要運行的任務(wù)。 當剛才交出執(zhí)行權(quán)的任務(wù)在后續(xù)收到數(shù)據(jù)庫事件響應(yīng)時, 事件循環(huán)會把它安排到就緒列表的第一個(不同的事件循環(huán)實現(xiàn)可能不一樣)并在下一次切換執(zhí)行權(quán)時, 把執(zhí)行權(quán)返回給他, 讓他繼續(xù)執(zhí)行, 直到遇到下一個等待事件。

這種切換協(xié)程的方式稱為協(xié)作式多任務(wù)處理, 由于只會在單個進程或者單個線程中運行, 切換協(xié)程時上下文是不用改變的, cpu不用重新讀寫緩存, 所以會節(jié)省一些開銷。 從上面可以看出協(xié)作式切換執(zhí)行權(quán)是基于協(xié)程自己主動讓出的, 而線程是搶占式的, 線程在沒遇到io事件時, 也可能從運行狀態(tài)轉(zhuǎn)為就緒狀態(tài), 直到再次被調(diào)用, 這樣會多出很多調(diào)度帶來的開銷, 而協(xié)程是會一直運行, 直到遇到讓步事件才切換, 所以協(xié)程調(diào)度的次數(shù)會比線程少很多。 同時可以看出協(xié)程的何時調(diào)度是由開發(fā)者指定(比如上面所說的等等數(shù)據(jù)庫返回事件), 而且是非搶占式的, 這就意味著某個協(xié)程在運行時, 其他協(xié)程是沒辦法運行的, 只能等到運行的協(xié)程交出執(zhí)行權(quán), 所以開發(fā)者要確保不能讓任務(wù)在cpu上停留太長時間,否則剩余的任務(wù)就會餓死。

3.總結(jié)

在io場景下, io的開銷比cpu執(zhí)行代碼邏輯外的開銷大很多, 從這里也可以換個想法思考, 在遇到io的開銷時, 代碼邏輯需要進行等待, 而cpu是空閑的, 于是就通過協(xié)程/線程的方式對于cpu的多路復(fù)用, 壓榨cpu。 假設(shè)sync語法和async語法執(zhí)行的代碼邏輯是一樣的, 那么他們執(zhí)行速度快慢的對比可以轉(zhuǎn)換為協(xié)程與多進程/線程的開銷對比, 也就是協(xié)程事件循環(huán)調(diào)度開銷與多進程/線程的調(diào)度的開銷邏輯對比, 而事件循環(huán)調(diào)度的開銷是基本不變(或者變化不大),多進程/線程的開銷除了比事件循環(huán)調(diào)度的開銷大外,還會隨著worker的量變多而變多, 當并發(fā)量高到一定程度時, 多進程/多線程的開銷會大于協(xié)程切換的開銷, 這時async語法的執(zhí)行速度就會快于sync語法。 所以在普通場景下, sync語法的執(zhí)行速度會快于async語法的執(zhí)行速度, 但在io計算大于cpu計算且高并發(fā)場景下時, async語法的執(zhí)行速度會比sync語法速度還快。

到此這篇關(guān)于詳解Python中Sync與Async執(zhí)行速度快慢對比的文章就介紹到這了,更多相關(guān)Python Sync Async內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • selenium+python截圖不成功的解決方法

    selenium+python截圖不成功的解決方法

    今天小編就為大家分享一篇selenium+python截圖不成功的解決方法 ,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-01-01
  • Python實現(xiàn)讀取及寫入csv文件的方法示例

    Python實現(xiàn)讀取及寫入csv文件的方法示例

    這篇文章主要介紹了Python實現(xiàn)讀取及寫入csv文件的方法,涉及Python針對csv格式文件的讀取、遍歷、寫入等相關(guān)操作技巧,需要的朋友可以參考下
    2018-01-01
  • 使用Python實現(xiàn)分組數(shù)據(jù)并保存到單獨的文件中

    使用Python實現(xiàn)分組數(shù)據(jù)并保存到單獨的文件中

    當處理大型數(shù)據(jù)集時,通常需要將數(shù)據(jù)分組,并將每個分組的數(shù)據(jù)保存到單獨的文件中,本文將使用 Python 中的 pandas 庫來實現(xiàn)這一目標,需要的可以參考下
    2024-04-04
  • 在Python web中實現(xiàn)驗證碼圖片代碼分享

    在Python web中實現(xiàn)驗證碼圖片代碼分享

    這篇文章主要介紹了在Python web中實現(xiàn)驗證碼圖片代碼分享,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • Python如何檢驗樣本是否服從正態(tài)分布

    Python如何檢驗樣本是否服從正態(tài)分布

    這篇文章主要介紹了Python如何檢驗樣本是否服從正態(tài)分布問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • Python實現(xiàn)一個論文下載器的過程

    Python實現(xiàn)一個論文下載器的過程

    這篇文章主要介紹了Python實現(xiàn)一個論文下載器的過程,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-01-01
  • Python編程flask使用頁面模版的方法

    Python編程flask使用頁面模版的方法

    今天小編就為大家分享一篇關(guān)于Python編程flask使用頁面模版的方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • python中py文件與pyc文件相互轉(zhuǎn)換的方法實例

    python中py文件與pyc文件相互轉(zhuǎn)換的方法實例

    pyc是一種二進制文件,是由py文件經(jīng)過編譯后,生成的文件,下面這篇文章主要給大家介紹了關(guān)于python中py文件與pyc文件相互轉(zhuǎn)換的相關(guān)資料,需要的朋友可以參考下
    2022-05-05
  • 90行Python代碼開發(fā)個人云盤應(yīng)用

    90行Python代碼開發(fā)個人云盤應(yīng)用

    這篇文章主要介紹了90行Python代碼開發(fā)個人云盤應(yīng)用,幫助大家更好的理解和學習python,感興趣的朋友可以了解下
    2021-04-04
  • Python利用matplotlib.pyplot繪圖時如何設(shè)置坐標軸刻度

    Python利用matplotlib.pyplot繪圖時如何設(shè)置坐標軸刻度

    Matplotlib是Python提供的一個二維繪圖庫,所有類型的平面圖,包括直方圖、散點圖、折線圖、點圖、熱圖以及其他各種類型,都能由Python制作出來。本文主要介紹了關(guān)于Python利用matplotlib.pyplot繪圖時如何設(shè)置坐標軸刻度的相關(guān)資料,需要的朋友可以參考下。
    2018-04-04

最新評論