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

提升python處理速度原理及方法實例

 更新時間:2019年12月25日 10:42:03   作者:夏末蟬未鳴  
這篇文章主要介紹了提升python處理速度原理及方法實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下

這篇文章主要介紹了提升python處理速度原理及方法實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下

 導(dǎo)讀:作為日常生產(chǎn)開發(fā)中非常實用的一門語言,python廣泛應(yīng)用于網(wǎng)絡(luò)爬蟲、web開發(fā)、自動化測試、數(shù)據(jù)分析和人工智能等領(lǐng)域。但python是單線程的,想要提升python的處理速度,涉及到一個很關(guān)鍵的技術(shù)——協(xié)程。本篇文章,將講述python協(xié)程的理解與使用。

1、操作系統(tǒng)相關(guān)概念

  在理解與使用協(xié)程之前,先簡單的了解幾個與操作系統(tǒng)相關(guān)的概念,包括進程、線程、同步和異步、阻塞與非阻塞。了解這些概念,對你學(xué)習(xí)協(xié)程、消息隊列、緩存等知識都有一定的幫助。

(1)進程:

  進程是操作系統(tǒng)分配資源的最小單位,系統(tǒng)由一個個程序(進程)組成的,一般而言,分為文本區(qū)域、數(shù)據(jù)區(qū)域和堆棧區(qū)域

  文本區(qū)域存儲處理器執(zhí)行的代碼(機器碼),通常來說,這是一個只讀區(qū)域,防止運行的程序被意外的修改

  數(shù)據(jù)區(qū)域存儲所有的變量和動態(tài)分配的內(nèi)存,又細分為初始化的數(shù)據(jù)區(qū)(所有初始化的全局、靜態(tài)、常量以及外部變量)和未初始化的數(shù)據(jù)區(qū)(初始化未0的全局變量和靜態(tài)變量),初始化的變量最初保存在文本區(qū),程序啟動后被拷貝到初始化的數(shù)據(jù)區(qū)

  堆棧區(qū)域存儲著活動過程調(diào)用的指令和本地變量,在地址空間里,棧區(qū)緊連著堆區(qū),他們的增長方向相反,內(nèi)存是線性的,所以我們的代碼放在低地址的地方,由低向高增長,棧區(qū)大小不可預(yù)測,隨開隨用,因此放在高地址的地方,由高向低增長。當(dāng)堆與棧指針重合的時候,意味著內(nèi)存耗盡,造成內(nèi)存溢出。

  進程的創(chuàng)建和銷毀都非常的消耗系統(tǒng)資源,是一種比較昂貴的操作。進程為了自身能夠得到運行,必須搶占式的爭奪CPU。對于單核CPU而言,在同一時間內(nèi)只能執(zhí)行一個進程的代碼,所以在單核CPU上實現(xiàn)多進程,是通過CPU的快速切換不同進程來實現(xiàn)的,看上去就像是多個進程同時執(zhí)行。

  由于進程間是隔離的,各自擁有自己的內(nèi)存資源,相比于線程的共享內(nèi)存而言,要更安全,不同進程之間的數(shù)據(jù)只能通過IPC(Inter-Process Communication)進行通信共享

(2)線程

  線程是CPU調(diào)度的基本單位。如果進程是一個容器,線程就是運行在容器里面的程序,線程是屬于進程的,同個進程的多個線程共享進程的內(nèi)存地址空間

  線程間可以直接通過全局變量進行通信,所以相對來說,線程間通信是不太安全的,因此引入各種鎖的場景,這里將不闡述

  當(dāng)一個線程奔潰了,會導(dǎo)致整個進程也奔潰,即其它線程也掛了。這一點與進程不一樣,一個進程掛了,其他進程照樣執(zhí)行

  在多核操作系統(tǒng)中,默認一個進程內(nèi)只有一個線程,所以對多進程處理就像是一個進程一個核心

(3)同步和異步

  同步和異步關(guān)注的是消息通信機制,所謂同步,就是在發(fā)出一個函數(shù)調(diào)用時,在沒有得到結(jié)果之前,該調(diào)用不會返回。一旦調(diào)用返回,就立即得到調(diào)用的返回值,即調(diào)用者主動等待調(diào)用結(jié)果

  所謂異步,就是在請求發(fā)出去后,這個調(diào)用就立即返回,但沒有返回結(jié)果,通過回調(diào)的方式告知該調(diào)用的實際結(jié)果

  同步的請求,需要主動讀寫數(shù)據(jù),并且等待結(jié)果;異步的請求,調(diào)用者不會立即得到結(jié)果。而是在調(diào)用發(fā)出后,被調(diào)用者通過狀態(tài)、通知來告訴調(diào)用者,或通過回調(diào)函數(shù)處理這個調(diào)用

(4)阻塞與非阻塞

  阻塞和非阻塞關(guān)注的是程序在等待調(diào)用結(jié)果(消息,返回值)時的狀態(tài)

  阻塞調(diào)用是指調(diào)用結(jié)果返回之前,當(dāng)前線程會被掛起。調(diào)用線程只有在得到結(jié)果之后才會返回

  非阻塞調(diào)用指在得到不能立即得到結(jié)果之前,該調(diào)用不會阻塞當(dāng)前線程。所以,區(qū)分的條件在于,進程/線程要訪問的數(shù)據(jù)是否就緒,進程/線程是否需要等待

  非阻塞一般通過多路復(fù)用實現(xiàn),多路復(fù)用由select、poll、epoll幾種實現(xiàn)方式

(5)協(xié)程

  了解完前面幾個概念,再來看看協(xié)程的概念

  協(xié)程是屬于線程的,又稱微線程,纖程,英文名是coroutine。舉個例子,在執(zhí)行函數(shù)A時,我希望能隨時終端去執(zhí)行函數(shù)B,然后終端B的執(zhí)行,切換回來執(zhí)行函數(shù)A。這就是協(xié)程的作用,由調(diào)用者自有切換。這個切換過程并不等同于函數(shù)調(diào)用,因為它沒有調(diào)用語句。執(zhí)行方式與多線程類似,但是協(xié)程只有一個線程執(zhí)行

  協(xié)程的優(yōu)點是執(zhí)行效率非常高,因為協(xié)程的切換是由程序自身控制,不需要切換線程,即沒有切換線程的開銷。同時,由于只有一個線程,不存在沖突的問題,不需要依賴鎖(加鎖和釋放鎖需要很多資源消耗)

  協(xié)程的主要使用場景在于處理io密集型程序,解決效率問題,不同于CPU密集型程序的處理。然而實際開發(fā)中這兩種場景非常多,如果要充分發(fā)揮CPU的利用率,可以使用多進程+協(xié)程的方式,本文后續(xù)將講到結(jié)合點

2、協(xié)程相關(guān)原理

  根據(jù)wikipedia的定義,協(xié)程是一個無優(yōu)先級的子程序調(diào)度組件,允許子程序在特定的地方掛起恢復(fù)。所以理論上,只要內(nèi)存足夠,一個線程可以有任意多個協(xié)程,但同一時刻只能有一個協(xié)程在運行,多個協(xié)程分享該線程分配到的計算機資源。協(xié)程是為了充分發(fā)揮異步調(diào)用的優(yōu)勢,異步操作則是為了IO操作阻塞線程

(1)知識準備

  在了解原理前,先做一個知識的準備

  1)現(xiàn)代主流的操作系統(tǒng)幾乎都是分時操作系統(tǒng),即一臺計算機采用時間片輪轉(zhuǎn)的方式為多個用戶提供服務(wù),系統(tǒng)資源分配的基本單位是進程,CPU調(diào)度的基本單位是線程

  2)運行時內(nèi)存空間氛圍變量區(qū)、棧區(qū)、堆區(qū)。內(nèi)存地址分配上,堆區(qū)從低到高,棧區(qū)從高到低

  3)計算機執(zhí)行時一條條指令讀取執(zhí)行,執(zhí)行到當(dāng)前指令時,下一條指令的指令的地址在指令寄存器的IP中,ESP寄存值只想當(dāng)前棧頂?shù)刂?,EBP指向當(dāng)前活動棧幀的基地址

  4)系統(tǒng)發(fā)生函數(shù)調(diào)用時操作為:先將入?yún)挠彝笠淮螇簵#缓蟀逊祷氐刂穳簵?,最后將?dāng)前EBP寄存器的值壓棧,修改ESP寄存器的值,在棧區(qū)分配當(dāng)前函數(shù)局部變量所需的空間

  5)協(xié)程的上下文包含屬于當(dāng)前協(xié)程的棧區(qū)和寄存器里面存放的值

(2)事件循環(huán)

  在python3.3中通過yield from使用協(xié)程,在3.5中,引入了關(guān)于協(xié)程的語法糖async/await的原理解析。其中,事件循環(huán)是一個核心所在,編寫過js的同學(xué),會對事件循環(huán)Eventloop更加了解,事件循環(huán)是一種等待程序分配消息或事件的編程架構(gòu)。在python中,asyncio.coroutine修飾器用來標記作為協(xié)程的函數(shù),這里的協(xié)程是和asyncio及其事件循環(huán)一起使用的,而在后續(xù)的發(fā)展中,async/await被使用的越來越廣泛

(3)async/await

  async/await是使用python協(xié)程的關(guān)鍵,從結(jié)構(gòu)上來看,asyncio實質(zhì)上是一個異步框架,async/await是為異步框架提供API以方便使用者調(diào)用,所以使用者要想使用async/await編寫協(xié)程代碼,目前必須基于asyncio或其他異步庫

(4)Future

  在實際開發(fā)編寫異步代碼時,為了避免太多回調(diào)方法導(dǎo)致的回調(diào)地獄,但又需要獲取異步調(diào)用的返回結(jié)果,聰明的語言設(shè)計者設(shè)計了一個叫做Future的對象,封裝了與loop的交互行為。其大致執(zhí)行過程為:程序啟動后,通過add_done_callback方法向epoll注冊回調(diào)函數(shù),當(dāng)result屬性得到返回值后,主動運行之前注冊的回調(diào)函數(shù),向上傳遞給coroutine。這個Future對象為asyncio.Future

  但是,要想取得返回值,程序必須恢復(fù)到工作狀態(tài),而由于Future對象本身的生存周期比較短,每一次注冊回調(diào)、產(chǎn)生事件、觸發(fā)回調(diào)過程后工作可能已經(jīng)完成,所以用Future向生成器send result并不合適。這里又引入一個新的對象Task,保存在Future對象中,對生成器協(xié)程進行狀態(tài)管理

  Python里另一個Future對象是concurrent.futures.Future,與asyncio.Future互不兼容,容易產(chǎn)生混淆。區(qū)別點在于,concurrent.futures是線程級的Future對象,當(dāng)使用concurrent.futures.Executor進行多線程編程時,該對象用于在不同的thread之間傳遞結(jié)果

(5)Task

  上文中提到,Task是維護生成器協(xié)程狀態(tài)處理執(zhí)行邏輯的任務(wù)對象,Task中有一個_step方法,負責(zé)生成器協(xié)程與EventLoop交互過程的狀態(tài)遷移,整個過程可以理解為:Task向協(xié)程send一個值,恢復(fù)其工作狀態(tài)。當(dāng)協(xié)程運行到斷點后,得到新的Future對象,再處理future與loop的回調(diào)注冊過程

(6)Loop

  在日常開發(fā)中,會有一個誤區(qū),認為每一個線程都可以有一個獨立的loop。實際運行時,主線程才能通過asyncio.get_event_loop()創(chuàng)建一個新的loop,而在其他線程時,使用get_event_loop()卻會拋錯。正確的做法為通過asyncio.set_event_loop(),將當(dāng)前線程與主線程loop顯式綁定

3、協(xié)程實戰(zhàn)

  上面介紹完了協(xié)程相關(guān)的概念和原理,接下來看看如何使用,這里舉一個實際場景的例子

場景:
  外部接受一些文件,每個文件里有一些數(shù)據(jù),其中,這組數(shù)據(jù)需要通過http的方式,發(fā)向第三方平臺,并獲得結(jié)果

分析:
  由于同一文件的每一組數(shù)據(jù)沒有前后的處理邏輯,在之前通過requests庫發(fā)送的網(wǎng)絡(luò)請求,串行執(zhí)行,下一組數(shù)據(jù)的發(fā)送需要等待上一組數(shù)據(jù)的返回,顯得整個文件的處理時間長,這種請求方式,完全可以由協(xié)程來實現(xiàn)

  為了更方便的配合協(xié)程發(fā)請求,我們使用aiohttp庫來代替requests庫,關(guān)于aiohttp,下面做簡單介紹

aiohttp:

  aiohttp是asyncio和python的異步HTTP客戶端/服務(wù)器,由于是異步的,經(jīng)常用在服務(wù)器端接收請求,和客戶端爬蟲應(yīng)用,發(fā)起異步請求,這里我們主要用來發(fā)請求

  aiohttp支持客戶端和HTTP服務(wù)器,可以實現(xiàn)單線程并發(fā)IO操作,無需使用Callback Hell即可支持Server WebSockets和Client WebSockets,且具有中間件

4、代碼實現(xiàn)

直接上代碼吧,talk is cheap,show me the code~

import aiohttp
import asyncio
from inspect import isfunction
import time
import logger

@logging_utils.exception(logger)
def request(pool, data_list):
  loop = asyncio.get_event_loop()
  loop.run_until_complete(exec(pool, data_list))
async def exec(pool, data_list):
  tasks = []
  sem = asyncio.Semaphore(pool)
  for item in data_list:
    tasks.append(
      control_sem(sem,
            item.get("method", "GET"),
            item.get("url"),
            item.get("data"),
            item.get("headers"),
            item.get("callback")))
  await asyncio.wait(tasks)
async def control_sem(sem, method, url, data, headers, callback):
  async with sem:
    count = 0
    flag = False
    while not flag and count < 4:
      flag = await fetch(method, url, data, headers, callback)
      count = count + 1
      print("flag:{},count:{}".format(flag, count))
    if count == 4 and not flag:
      raise Exception('EAS service not responding after 4 times of retry.')
async def fetch(method, url, data, headers, callback):
  async with aiohttp.request(method, url=url, data=data, headers=headers) as resp:
    try:
      json = await resp.read()
      print(json)
      if resp.status != 200:
        return False
      if isfunction(callback):
        callback(json)
      return True
    except Exception as e:
      print(e)

這里,我們封裝了對外發(fā)送批量請求的request方法,接收一次性發(fā)送的數(shù)據(jù)多少,和數(shù)據(jù)綜合,在外部使用時,只需要構(gòu)建好網(wǎng)絡(luò)請求對象的數(shù)據(jù),設(shè)定好請求池大小即可,同時,設(shè)置了重試功能,進行了4次重試,防治在網(wǎng)絡(luò)抖動的時候,單個數(shù)據(jù)的網(wǎng)絡(luò)請求發(fā)送失敗

最終效果:

在使用協(xié)程重構(gòu)網(wǎng)絡(luò)請求模塊之后,當(dāng)數(shù)據(jù)量在1000的時候,由之前的816s,提升到424s,快了一倍,且請求池大小加大的時候,效果更明顯,由于第三方平臺同時建立連接的數(shù)據(jù)限制,我們設(shè)定了40的閾值??梢钥吹?,優(yōu)化的程度很顯著

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 解析Pytest3種配置文件方式

    解析Pytest3種配置文件方式

    pytest的主配置文件,可以改變pytest的默認行為,本文主要介紹了解析Pytest3種配置文件方式,具有一定的參考價值,感興趣的可以了解一下
    2024-02-02
  • Python使用Mechanize模塊編寫爬蟲的要點解析

    Python使用Mechanize模塊編寫爬蟲的要點解析

    這篇文章主要介紹了Python使用Mechanize模塊編寫爬蟲的要點解析,作者還講解了Mechanize程序占用內(nèi)存過高問題的相關(guān)解決方法,需要的朋友可以參考下
    2016-03-03
  • pycharm中顯示CSS提示的知識點總結(jié)

    pycharm中顯示CSS提示的知識點總結(jié)

    在本篇文章里小編給大家整理了關(guān)于pycharm中顯示CSS提示的知識點內(nèi)容,需要的朋友們可以參考學(xué)習(xí)下。
    2019-07-07
  • tensorboard顯示空白的解決

    tensorboard顯示空白的解決

    今天小編就為大家分享一篇tensorboard顯示空白的解決,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-02-02
  • Python 使用folium繪制leaflet地圖的實現(xiàn)方法

    Python 使用folium繪制leaflet地圖的實現(xiàn)方法

    今天小編就為大家分享一篇Python 使用folium繪制leaflet地圖的實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-07-07
  • python2.7實現(xiàn)爬蟲網(wǎng)頁數(shù)據(jù)

    python2.7實現(xiàn)爬蟲網(wǎng)頁數(shù)據(jù)

    這篇文章主要為大家詳細介紹了python2.7實現(xiàn)爬蟲網(wǎng)頁數(shù)據(jù),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • 基于OpenCV的路面質(zhì)量檢測的實現(xiàn)

    基于OpenCV的路面質(zhì)量檢測的實現(xiàn)

    這篇文章主要介紹了基于OpenCV的路面質(zhì)量檢測,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • Caffe卷積神經(jīng)網(wǎng)絡(luò)視覺層Vision?Layers及參數(shù)詳解

    Caffe卷積神經(jīng)網(wǎng)絡(luò)視覺層Vision?Layers及參數(shù)詳解

    這篇文章主要為大家介紹了Caffe卷積神經(jīng)網(wǎng)絡(luò)視覺層Vision?Layers及參數(shù)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • 分享10個有趣的Python程序

    分享10個有趣的Python程序

    這篇文章主要給大家分享的是10個有趣的Python程序,Python程序有許多模塊和第三方包,這非常有助于高效編程,所以了解這些模塊的正確使用方法是很重要的,下面詳細內(nèi)容,需要的小伙伴可以參考一下
    2022-02-02
  • python統(tǒng)計多維數(shù)組的行數(shù)和列數(shù)實例

    python統(tǒng)計多維數(shù)組的行數(shù)和列數(shù)實例

    今天小編就為大家分享一篇python統(tǒng)計多維數(shù)組的行數(shù)和列數(shù)實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-06-06

最新評論