中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何提升python處理速度

發布時間:2021-08-03 12:36:58 來源:億速云 閱讀:178 作者:小新 欄目:開發技術

這篇文章主要介紹如何提升python處理速度,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

1、操作系統相關概念

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

(1)進程:

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

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

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

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

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

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

(2)線程

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

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

  當一個線程奔潰了,會導致整個進程也奔潰,即其它線程也掛了。這一點與進程不一樣,一個進程掛了,其他進程照樣執行

  在多核操作系統中,默認一個進程內只有一個線程,所以對多進程處理就像是一個進程一個核心

(3)同步和異步

  同步和異步關注的是消息通信機制,所謂同步,就是在發出一個函數調用時,在沒有得到結果之前,該調用不會返回。一旦調用返回,就立即得到調用的返回值,即調用者主動等待調用結果

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

  同步的請求,需要主動讀寫數據,并且等待結果;異步的請求,調用者不會立即得到結果。而是在調用發出后,被調用者通過狀態、通知來告訴調用者,或通過回調函數處理這個調用

(4)阻塞與非阻塞

  阻塞和非阻塞關注的是程序在等待調用結果(消息,返回值)時的狀態

  阻塞調用是指調用結果返回之前,當前線程會被掛起。調用線程只有在得到結果之后才會返回

  非阻塞調用指在得到不能立即得到結果之前,該調用不會阻塞當前線程。所以,區分的條件在于,進程/線程要訪問的數據是否就緒,進程/線程是否需要等待

  非阻塞一般通過多路復用實現,多路復用由select、poll、epoll幾種實現方式

(5)協程

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

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

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

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

2、協程相關原理

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

(1)知識準備

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

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

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

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

  4)系統發生函數調用時操作為:先將入參從右往左一次壓棧,然后把返回地址壓棧,最后將當前EBP寄存器的值壓棧,修改ESP寄存器的值,在棧區分配當前函數局部變量所需的空間

  5)協程的上下文包含屬于當前協程的棧區和寄存器里面存放的值

(2)事件循環

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

(3)async/await

  async/await是使用python協程的關鍵,從結構上來看,asyncio實質上是一個異步框架,async/await是為異步框架提供API以方便使用者調用,所以使用者要想使用async/await編寫協程代碼,目前必須基于asyncio或其他異步庫

(4)Future

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

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

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

(5)Task

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

(6)Loop

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

3、協程實戰

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

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

分析:
  由于同一文件的每一組數據沒有前后的處理邏輯,在之前通過requests庫發送的網絡請求,串行執行,下一組數據的發送需要等待上一組數據的返回,顯得整個文件的處理時間長,這種請求方式,完全可以由協程來實現

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

aiohttp:

  aiohttp是asyncio和python的異步HTTP客戶端/服務器,由于是異步的,經常用在服務器端接收請求,和客戶端爬蟲應用,發起異步請求,這里我們主要用來發請求

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

4、代碼實現

直接上代碼吧,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)

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

最終效果:

在使用協程重構網絡請求模塊之后,當數據量在1000的時候,由之前的816s,提升到424s,快了一倍,且請求池大小加大的時候,效果更明顯,由于第三方平臺同時建立連接的數據限制,我們設定了40的閾值。可以看到,優化的程度很顯著

以上是“如何提升python處理速度”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

夏津县| 荣昌县| 县级市| 铜山县| 南城县| 鞍山市| 讷河市| 昭平县| 清原| 清丰县| 天祝| 沾益县| 蓬溪县| 大庆市| 大冶市| 三穗县| 抚松县| 颍上县| 嘉兴市| 武城县| 泉州市| 西峡县| 北海市| 宁南县| 长寿区| 弋阳县| 奉新县| 苏尼特左旗| 班戈县| 淮阳县| 靖宇县| 金阳县| 夏津县| 浑源县| 南澳县| 金湖县| 永登县| 阳谷县| 德令哈市| 天峨县| 明星|