亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Python:

系統 2278 0

《python源碼剖析》閱讀筆記

第一章 python的內建對象

python中一切都是對象。在PyIntObject中定義了很多函數指針,這些函數指針對應著類型對象所定義的操作。其中有三組非常重要的操作族,tp_as_number, tp_as_sequence, tp_as_mapping,分別對應著PyNumberMethods, PySequenceMethods, PyMappingMethods函數族,這三個函數都是分別定義著一個整數對象、序列對象、關聯對象應該有的一些操作。在python中對于任何一種類型來說,都可以同時定義三個函數族中的所有操作,也就是說,我們可以在繼承python固有的類型時,又同時重寫python的special method,從而實現我們自己定義的特殊類型。比如可以實現一個對象,既可以表現出數值特性,也可以表現出關聯對象的屬性。

第二章 python中的整數對象

python的整數對象。在python中會有一個整數對象池,里面維護著-5到256之間的整數,當我們需要使用這些范圍的數字時,直接到對象池中去提取。這樣設計是因為在實際的開發中,這些小數可能會被頻繁的使用,頻繁的創建和湮滅會導致效率非常低。對于超出這些范圍的數字,python底層則是通過單獨申請一塊內存塊來給這些大整數輪流使用,當某個對象的引用次數為零的時候,會把該對象所占用的這塊內存添加到空閑區(注意,這里python并沒有將釋放的內存還給系統,所以當出現很多大數要被使用的時候,而這些數又是使用一次,可能就會占用系統的所有內存。)

第三章 python中的字符串對象

  1. 在python中,PyStringObject是字符串對象的實現,它是一個可變長度內存的對象,同時又是一個不可變對象(字符串定義之后不可再改變)。python字符串對象中的intern機制,類似于前面的python整數對象池。其關鍵是在于在系統中有一個(key, value)的映射關系集合,集合名字叫做Intered。在這個集合中記錄過被intern機制處理過的字符串。當python在創建一個字符串時,首先會建立一個PyStringObject對象a,然后利用intern機制處理,如果當前這個字符串對象已經在interned中存在,記為b,則將指向a的對象的指針指向b,然后intern機制會將對象a的引用計數減為0而被銷毀。這樣的話,可以達到減少內存的目的。
    也許會有一個問題,為什么一定要先創建一個臨時PyStringObject對象呢?
    這是因為intern機制只能應用在PyStringObject對象之上,別的任何對象都不可以。因此這里必須要先創建一個臨時對象。
  2. python中也為一個字節的字符設計了字符緩沖池,與小整數對象池不一樣的是,小整數對象池在python初始化的時候就會被創建,而字符串對象中的字符緩沖池是以靜態變量形式存在的,在python初始化完成之后,字符緩沖池是空的。當python在創建一個PyStringObject的時候,如果字符串長度為1(即單個字符串),則將會對這個字符進行intern操作,將intern的這個結果緩存到字符緩沖池中。當通過PyString_FromStringandSize(const char *str, int size)來創建字符對象時,如果size等于1,并且在字符緩沖池中存在,則直接返回。
  3. python中字符串的連接效率問題。當我們采用+號來連接多個字符串時,效率是非常低的。其根源在于,python中的字符串對象是一個不可變對象,因此在連接兩個字符串的時候,則要去新建一個PyStringObject對象,當我們用‘+’來連接N個字符串時,實際上會進行N-1次內存申請和內存搬運,因而效率很低。 解決方案是,我們采用字符串對象的join操作來實現多個字符串的連接,當使用join操作的時候,會一次性計算好需要的總的內存,也就是只需要執行一次內存申請,因此效率會比用‘+’號連接的效率更高
            
              strs_list = ['hello', 'fay', 'welcome', 'to', 'python ']
' '.join(strs_list)
            
          

https://www.cnblogs.com/megachen/p/9156646.html

Python源碼讀后小結

?

?


?

《改善Python程序的91個建議》

訪問flyai.club,一鍵創建你的人工智能項目。

作者 | 笑虎

來源 | 代碼灣

自己寫Python也有四五年了,一直是用自己的“強迫癥”在維持自己代碼的質量,除了Google的Python代碼規范外,從來沒有讀過類似的書籍。偶然的機會看到這么一本書,讀完之后覺得還不錯,所以做個簡單的筆記。有想學習類似知識的朋友,又懶得去讀完整本書籍,可以參考一下。

1:引論

建議1、理解Pythonic概念—-詳見Python中的《Python之禪》

建議2、編寫Pythonic代碼

避免不規范代碼,比如只用大小寫區分變量、使用容易混淆的變量名、害怕過長變量名等。有時候長的變量名會使代碼更加具有可讀性。

深入學習Python相關知識,比如語言特性、庫特性等,比如Python演變過程等。 深入學習一兩個業內公認的Pythonic的代碼庫,比如Flask等。

https://angelteng.github.io/blog/2019/07/25/Flask源碼學習/

?

建議3:理解Python與C的不同之處,比如縮進與{},單引號雙引號,三元操作符?,Switch-Case語句等。

建議4:在代碼中適當添加注釋

建議5:適當添加空行使代碼布局更加合理

建議6:編寫函數的4個原則

函數設計要盡量短小,嵌套層次不宜過深

函數聲明應該做到合理、簡單、易用

函數參數設計應該考慮向下兼容

一個函數只做一件事,盡量保證函數粒度的一致性

建議7:將常量集中在一個文件,且常量名盡量使用全大寫字母

2:編程慣用法

建議8:利用assert語句來發現問題,但要注意,斷言assert會影響效率

建議9:數據交換值時不推薦使用臨時變量,而是直接a, b = b, a

建議10:充分利用惰性計算(Lazy evaluation)的特性,從而避免不必要的計算

?

建議11:理解枚舉替代實現的缺陷(最新版Python中已經加入了枚舉特性)

建議12:不推薦使用type來進行類型檢查,因為有些時候type的結果并不一定可靠。如果有需求,建議使用isinstance函數來代替

建議13:盡量將變量轉化為浮點類型后再做除法(Python3以后不用考慮)

建議14:警惕eval()函數的安全漏洞,有點類似于SQL注入

建議15:使用enumerate()同時獲取序列迭代的索引和值

建議16:分清==和is的適用場景,特別是在比較字符串等不可變類型變量時

建議17:盡量使用Unicode。在Python2中編碼是很讓人頭痛的一件事,但Python3就不用過多考慮了

建議18:構建合理的包層次來管理Module

3:基礎用法

建議19:有節制的使用from…import語句,防止污染命名空間

建議20:優先使用absolute import來導入模塊(Python3中已經移除了relative import)

建議21:i+=1不等于++i,在Python中,++i前邊的加號僅表示正,不表示操作

建議22:習慣使用with自動關閉資源,特別是在文件讀寫中

建議23:使用else子句簡化循環(異常處理)

建議24:遵循異常處理的幾點基本原則

注意異常的粒度,try塊中盡量少寫代碼

謹慎使用單獨的except語句,或except Exception語句,而是定位到具體異常

注意異常捕獲的順序,在合適的層次處理異常

使用更加友好的異常信息,遵守異常參數的規范

建議25:避免finally中可能發生的陷阱

建議26:深入理解None,正確判斷對象是否為空。Python中下列數據會判斷為空:

Python:_第1張圖片

?

建議27:連接字符串應優先使用join函數,而不是+操作

建議28:格式化字符串時盡量使用.format函數,而不是%形式

建議29:區別對待可變對象和不可變對象,特別是作為函數參數時

建議30:[], {}和():一致的容器初始化形式。使用列表解析可以使代碼更清晰,同時效率更高

?

建議31:函數傳參數,既不是傳值也不是傳引用,而是傳對象或者說對象的引用

建議32:警惕默認參數潛在的問題,特別是當默認參數為可變對象時

建議33:函數中慎用變長參數*args和**kargs

這種使用太靈活,從而使得函數簽名不夠清晰,可讀性較差

如果因為函數參數過多而是用變長參數簡化函數定義,那么一般該函數可以重構

建議34:深入理解str()和repr()的區別

兩者之間的目標不同:str主要面向客戶,其目的是可讀性,返回形式為用戶友好性和可讀性都比較高的字符串形式;而repr是面向Python解釋器或者說Python開發人員,其目的是準確性,其返回值表示Python解釋器內部的定義

?

在解釋器中直接輸入變量,默認調用repr函數,而print(var)默認調用str函數

repr函數的返回值一般可以用eval函數來還原對象

兩者分別調用對象的內建函數__str__()和__repr__()

建議35:分清靜態方法staticmethod和類方法classmethod的使用場景

4:庫

建議36:掌握字符串的基本用法

建議37:按需選擇sort()和sorted()函數

sort()是列表在就地進行排序,所以不能排序元組等不可變類型。

sorted()可以排序任意的可迭代類型,同時不改變原變量本身。

建議38:使用copy模塊深拷貝對象,區分淺拷貝(shallow copy)和深拷貝(deep copy)

建議39:使用Counter進行計數統計,Counter是字典類的子類,在collections模塊中

建議40:深入掌握ConfigParser

建議41:使用argparse模塊處理命令行參數

建議42:使用pandas處理大型CSV文件

Python本身提供一個CSV文件處理模塊,并提供reader、writer等函數。

Pandas可提供分塊、合并處理等,適用于數據量大的情況,且對二維數據操作更方便。

建議43:使用ElementTree解析XML

建議44:理解模塊pickle的優劣

優勢:接口簡單、各平臺通用、支持的數據類型廣泛、擴展性強

劣勢:不保證數據操作的原子性、存在安全問題、不同語言之間不兼容

建議45:序列化的另一個選擇JSON模塊:load和dump操作

建議46:使用traceback獲取棧信息

建議47:使用logging記錄日志信息

建議48:使用threading模塊編寫多線程程序

建議49:使用Queue模塊使多線程編程更安全

5:設計模式

建議50:利用模塊實現單例模式

?

建議51:用mixin模式讓程序更加靈活

建議52:用發布-訂閱模式實現松耦合

建議53:用狀態模式美化代碼

?

6:內部機制

建議54:理解build-in對象

建議55:__init__()不是構造方法,理解__new__()與它之間的區別

?

建議56:理解變量的查找機制,即作用域

局部作用域

全局作用域

嵌套作用域

內置作用域

建議57:為什么需要self參數

建議58:理解MRO(方法解析順序)與多繼承

?

建議59:理解描述符機制

建議60:區別__getattr__()與__getattribute__()方法之間的區別

?

建議61:使用更安全的property

建議62:掌握元類metaclass

建議63:熟悉Python對象協議

Python:_第2張圖片

?

建議64:利用操作符重載實現中綴語法

?

建議65:熟悉Python的迭代器協議

建議66:熟悉Python的生成器

建議67:基于生成器的協程和 greenlet ,理解協程、多線程、多進程之間的區別

?

建議68:理解GIL的局限性

建議69:對象的管理和垃圾回收

7:使用工具輔助項目開發

建議70:從PyPI安裝第三方包

建議71:使用pip和yolk安裝、管理包

建議72:做paster創建包

建議73:理解單元測試的概念

建議74:為包編寫單元測試

建議75:利用測試驅動開發(TDD)提高代碼的可測性

建議76:使用Pylint檢查代碼風格

代碼風格審查

代碼錯誤檢查

發現重復以及不合理的代碼,方便重構

高度的可配置化和可定制化

支持各種IDE和編輯器的集成

能夠基于Python代碼生成UML圖

能夠與Jenkins等持續集成工具相結合,支持自動代碼審查

建議77:進行高效的代碼審查

建議78:將包發布到PyPI

8:性能剖析與優化

建議79:了解代碼優化的基本原則

建議80:借助性能優化工具

建議81:利用cProfile定位性能瓶頸

建議82:使用memory_profiler和objgraph剖析內存使用

建議83:努力降低算法復雜度

建議84:掌握循環優化的基本技巧

減少循環內部的計算

將顯式循環改為隱式循環,當然這會犧牲代碼的可讀性

在循環中盡量引用局部變量

關注內層嵌套循環

建議85:使用生成器提高效率

建議86:使用不同的數據結構優化性能

建議87:充分利用set的優勢

建議88:使用multiprocessing模塊克服GIL缺陷

建議89:使用線程池提高效率

建議90:使用C/C++模塊擴展提高性能

建議91:使用Cythonb編寫擴展模塊

訪問 flyai.club ,一鍵創建你的人工智能項目。

End


?

《Python 面向對象編程指南》

?

Python 面向對象編程指南(高清版) PDF

?

百度網盤

鏈接: https://pan.baidu.com/s/1SbD4gum4yGcUruH9icTPCQ

提取碼: fzk5

?

內置方法 ? ? 說明
?__init__(self,...) ? ? 初始化對象,在創建新對象時調用
?__del__(self) ? ? 釋放對象,在對象被刪除之前調用
?__new__(cls,*args,**kwd) ? ? 實例的生成操作
?__str__(self) ? ? 在使用print語句時被調用
?__getitem__(self,key) ? ? 獲取序列的索引key對應的值,等價于seq[key]
?__len__(self) ? ? 在調用內聯函數len()時被調用
?__cmp__(stc,dst) ? ? 比較兩個對象src和dst
?__getattr__(s,name) ? ? 獲取屬性的值
?__setattr__(s,name,value) ? ? 設置屬性的值
?__delattr__(s,name) ? ? 刪除name屬性
?__getattribute__() ? ? __getattribute__()功能與__getattr__()類似
?__gt__(self,other) ? ? 判斷self對象是否大于other對象
?__lt__(slef,other) ? ? 判斷self對象是否小于other對象
?__ge__(slef,other) ? ? 判斷self對象是否大于或者等于other對象
?__le__(slef,other) ? ? 判斷self對象是否小于或者等于other對象
?__eq__(slef,other) ? ? 判斷self對象是否等于other對象
?__call__(self,*args) ? ? 把實例對象作為函數調用

?

https://blog.csdn.net/huangyimo/article/details/50561841

?


?

?

?

??

?

?


?

《Python 高性能編程》

python高性能編程第一章讀書筆記

計算機底層組件分為三大基本部分:計算單元、存儲單元以及兩者之間的連接。

計算單元:具有將接收到的任意輸入轉換成輸出的能力以及改變當前處理狀態的能力。CPU是最常見的計算單元。它的主要屬性是其每個周期能進行的操作數量以及每秒能完成多少個周期。第一個屬性通過每周期完成的指令數(IPC)來衡量。第二個屬性通過其時鐘速度來衡量。時鐘速度的提高,可以使得每秒進行更多的計算,提高該計算單元所有程序的運行速度。IPC的提高則在矢量計算能力上有相當程度的影響。矢量計算指的是一次提供多個數據給一個CPU并能同時被操作。這種類型的CPU指令被稱為SIMD(單指令多數據)。

???????? 由于時鐘速度和IPC提升陷入停滯,開始 依靠超線程技術,亂序執行和多核架構來提高速度 。超線程技術為主機的操作系統(OS)虛擬了第二個CPU,硬件邏輯則試圖將兩個指令線程交錯地插入單個CPU的執行單元。亂序執行允許編譯器檢測出一個線性程序中某部分可以不依賴于之前的工作,也就是說兩個工作能夠以各種順序執行或同時執行。使得當一些指令被阻塞(比如等待一次內存訪問),另一些指令得以執行。多核架構指的是給CPU增加更多的核心。但是不一定多核就會更快,阿姆達爾定律認為:如果一個可以運行在多核上的程序有某些執行路徑必須運行在單核上,那么這些路徑就會成為瓶頸導致最終速度無法加快。

???????? 對于python來說,python的全局解釋器鎖(GIL),確保Python進程一次只能執行一條指令,無論當前有多少個核心。使得無法使用多核,但是我們可以 使用標準庫的multiprocessing,或numexpr、Cython技術,或分布式計算模型來避免

???????? 存儲單元,用于保存比特。比如主板上的寄存器、RAM以及硬盤。所有這些不同類型的存儲單元主要區別在于讀寫數據的速度。且速度與讀寫方式有關,如順序讀取要比隨機讀取快得多。此外還有延時來表示設備查找數據所花費的時間。

???????? 比如說判斷一個數是否為質數,首先將number的值保存在RAM中。為了計算sqrt_number和number_float,將該值傳入CPU中,理想情況下只需要傳一次,它將被保存在CPU的L1/L2緩存中,然后CPU進行兩次計算并將結果傳回RAM保存。在循環部分,我們 更希望一次就將 number——float和 多個i的值傳入CPU進行檢查

???????? Python虛擬機抽象層的影響之一就是矢量操作變得不是直接可用 。而numpy這樣的外部庫可以 通過增加矢量化數學操作 來幫助我們解決這個問題。

???????? Python抽象還影響了任何需要為下一次計算保存L1/L2緩存中相關數據的優化。首先是python對象不再是內存中最優化的布局。這是因為python是一種垃圾收集語言----內存會被自動分配并在需要時釋放。這會導致內存碎片并影響CPU緩存的傳輸。

???????? Python的優勢在于可以輕易調用其他系統, 正確運用庫可以使python代碼在速度上和c媲美。

?

?

Python 高性能編程第4章 字典和集合

對次序未知的列表/元組的最優查詢時間O(logn),字典和集合 基于鍵的查詢 則可以帶給我們O(1)的查詢時間。除此之外,和列表/元組一樣,字典和集合的插入時間O(1)。為了達到O(1)的查詢時間,在底層使用的數據結構是一個開放地址散列表。然而,使用字典和集合有其代價,首先通常會占用更多的內存。同時雖然插入/查詢的復雜度是O(1),但實際的速度極大取決于使用的散列函數。如果散列函數的運行速度較慢,那么在字典和集合上進行的任何操作也會相應變慢。

集合保證了它包含的鍵的唯一性,如果嘗試添加一個已有的項,該項不會被添加進集合,這一操作的代價是O(1),而列表進行相應操作的代價是O(n)

對于散列表,新插入數據的位置取決于數據的兩個屬性:鍵的散列值以及該值如何跟其他對象比較。這是因為當我們插入數據時,首先需要計算鍵的散列值并掩碼來得到一個有效的數據索引。掩碼是為了保證一個可能是任意數字的散列值最終能落入分配的桶中。所以,如果我們分配了8個塊的內存,而我們的散列值是28957,那么它將落入的桶的索引是28957&0b111 = 7,如果我們的字典增長到了需要512塊內存,那么掩碼就變成了0b11111111(此時我們會使用28957&0b11111111的桶)。現在我們檢查這個桶是否已經被使用,如果是空桶,那么可以將鍵和值插入這一內存塊。我們保存鍵是為了在獲取時確保獲得的是正確的值。如果桶已經被使用,且桶內的值和我們希望插入的值相等,說明這一鍵值對已經被保存,則直接返回。如果值不相等,則找一個新的位置來保存位置。

為了找到新的索引,我們用一個 簡單的線性函數計算出一個新的索引,這一方法稱為嗅探 Pyhon的嗅探機制使用了原始散列值的高位比特 (對于之前那個長度為8的散列表,由于使用的掩碼mask=0b111=bin(8-1),我們只用了最后3個bit作為初始索引)。使用這些 高位比特使得每一個散列值生成的下一可用散列序列都是不同的 ,這樣就能幫助防止未來的碰撞。

當我們在查詢某個鍵時也有一個類似的過程:給出的鍵會被轉化為一個索引進行檢索。如果和該索引指向的位置中的鍵符合(在插入操作時我們會保存原始的鍵),那么我們就會返回那個值。如果不符合,我們用同一方案繼續創建新的索引,直到我們找到數據或找到一個空桶。如果我們找到一個空桶,我們就可以認為表里不存在該數據。

當越來越多的項目被插入列表時,表本身必須改變大小來適應。研究顯示 一個不超過三分之二滿的表在具有最佳空間節約的同時,依然具有不錯的散列碰撞避免率 。因此,當一個表到達關鍵點時,它就會增長。為了做到這一點,需要分配一個更大的表(也就是在內存中預留更多的桶),將掩碼調整為適合新的表,舊表中的所有元素被重新插入新表。這需要重新計算索引,因為改變后的掩碼會改變索引計算結果。結果就是, 改大散列表的代價非常大! 不過因為我們只在表太小時而不是在每一次操作時進行這一操作,分攤后每一次插入的代價依然是O(1)。值得注意的是,當一個散列表變大或變小時都可能發生改變大小。也就是說,如果散列表中足夠多的元素被刪除,表可能會被改小,但是 改變大小僅發生在插入時

每當Python訪問一個變量、函數或模塊時,都有一個體系來決定它去哪里查找這些對象。首先Python查找 locals()數組 ,其內保存了所有本地變量的條目。這是整條鏈上唯一一個不需要字典查詢的部分。如果它不再本地變量里,那么會搜索 globals()字典 。最后如果對象也不在那里,則會搜索 __builtin__對象 。要注意locals()和globals()是顯式的字典而__builtin__則是模塊對象,在搜索__builtin__中的一個屬性時,我們其實在搜索它的locals(字典)(對所有的模塊對象和類對象都是如此)。


?

《Python高性能編程》筆記

2019-07-23

|?In?Python

性能分析

  1. 基本技術如 IPython 的 timeit 魔法函數、time.time()、以及一個計時修飾器,使用這些技術來了解語句和函數的行為。
  2. 內置工具如 cProfile,了解代碼中哪些函數耗時最長,并用 runsnake 進行可視化。
  3. line_profiler 工具,對選定的函數進行逐行分析,其結果包含每行被調用的次數以及每行花費的時間百分比。
  4. memory_profiler 工具,以圖的形式展示RAM的使用情況隨時間的變化,解釋為什么某個函數占用了比預期更多的 RAM。
  5. Guppy 項目的 heapy 工具,查看 Python 堆中對象的數量以及每個對象的大小,這 對于消滅奇怪的內存泄漏特別有用
  6. dowser 工具,通過Web瀏覽器界面審查一個持續運行的進程中的實時對象。
  7. dis 模塊,查看 CPython 的字節碼,了解基于棧的 Python 虛擬機如何運行。
  8. 單元測試,在性能分析時要避免由優化手段帶來的破壞性后果。(參見原文,實用)

數據結構影響

  1. 列表和元組就類似于其它編程語言的數組,主要用于存儲具有內在次序的數據;

    1. 高效搜索必需的兩大要素是排序算法和搜索算法。Python 列表有一個內建的排序算法使用了Tim排序。
    2. 動態數組支持 resize 操作,可以增加數組的容量。當一個大小為N的列表第一次需要添加數據時,Python會創建一個新的列表,足夠存放原來的N個元素以及額外需要添加的元素。
    3. 元組固定且不可變。這意味著一旦元組被創建,和列表不同,它的內容無法被修改或它的大小也無法被改變。(

      元組緩存于Python運行時環境,這意味著我們每次使用元組時無須訪問內核去分配內存。)

    4. bi-sect 模塊:提供了一個簡便的函數讓你可以在保持排序的同時往列表中添加元素,以及一個高度優化過的二分搜索算法函數來查找元素。

字典和集合就類似其它編程語言的哈希表/散列集,主要用于存儲無序的數據。

    1. 新插入數據的位置取決于數據的兩個屬性:鍵的散列值以及該值如何跟其他對象比較。這是因為當我們插入數據時,首先需要計算鍵的散列值并掩碼來得到一個有效的數組索引。
    2. 如果被占用,那么要找到新的索引,我們用一個簡單的線性函數計算出一個新的索引,這一方法稱為嗅探。Python的嗅探機制使用了原始散列值的高位比特。使用這些高位比特使得每一個散列值生成的下一可用散列序列都是不同的,這樣就能幫助防止未來的碰撞。
    3. 當一個值從散列表中被刪除時,我們不能簡單地寫一個NULL到內存的那個桶里。這是因為我們已經用NULL來作為嗅探散列碰撞的終止值。所以,我們必須寫一個特殊的值來表示該桶雖空,但其后可能還有別的因散列碰撞而插入的值。
    4. 不超過三分之二滿的表在具有最佳空間節約的同時依然具有不錯的散列碰撞避免率 改大散列表的代價非常昂貴 ,但因為我們只在表太小時而不是在每一次插入時進行這一操作。
    5. Python 對象通常以散列表實現,因為它們已經有 內建的 hash cmp 函數
    6. 每當 Python 訪問一個變量、函數或模塊時,都有一個體系來決定它去哪里查找這些對象。
      1. locals()數組
      2. globals()字典
      3. __builtin__對象( builtin 中的一個 屬性時,我們其實是在搜索它的 locals()字典)

迭代器、生成器

  1. 節約內存
  2. 延遲估值

矩陣與矢量計算

原生 Python 并不支持矢量操作,因為 Python 列表存儲的不是實際的數據,而是對實際數據的引用 且Python 字節碼并沒有針對矢量操作進行優化,所以for 循環無法預測何時使用矢量操作能帶來好處 在矢量和矩陣操作時,這種存儲結構會造成極大的性能下降

?

6.3 內存碎片:

?

數據被分成小片,你只能對每一片分別進行傳輸,而不是一次性傳輸整個塊。這意 味著你引入了更多的內存傳輸開銷,且強制?CPU?在數據傳輸的過程中等待。我們 可以用?perf?看到在緩存失效的情況下這個問題會有多嚴重。

這個在正確的時候將正確的數據傳輸給?CPU?的問題被稱為“ 馮諾伊曼瓶頸 ”。意思 是 現代計算機所使用的層次化的內存架構會導致?CPU?和內存之間的帶寬受到限 制 。如果我們數據傳輸的速度可以無限快,我們就不需要任何緩存,因為?CPU?可 以立即獲得任何它需要的數據。此時瓶頸就不再存在。

由于數據傳輸的速度不可能無限快,我們必須從?RAM?中預取數據并將其保存在 一個更小但更快的?CPU?緩存中,并希望當?CPU?需要某個數據時,它可以從中更 快讀取到。雖然這已經是一個嚴重理想化了的場景,我們依然可以看到其中的一 些問題—?我們如何知道未來需要哪些數據?CPU?內部的 分支預測和流水線技 術會試圖在處理當前指令的同時預測其下一條指令并將相應的內存讀進緩存 。但 是 減少瓶頸最好的方法是讓代碼知道如何分配我們的內存以及如何使用我們的 數據進行計算

探測內存移動至?CPU?的性能相當困難 ,不過, Linux?上的?perf?工具可以讓我們 洞察?CPU?如何處理運行中的程序 。比如,我們可以對例?6-6?的純?Python?代碼運 行?perf?并看到?CPU?運行我們代碼的效率。結果見例?6-8。注意該例以及之后的perf?例子的輸出都被截取以適應頁面邊界。被刪除的數據包括各測量值的方差, 用于表示測量值在幾次測量中發生了多大的變化。這有助于看到測量值在多大程 度上依賴于實際的程序特性以及多大程度上受到來自系統的其他干擾,比如其他 正在使用系統資源的程序。

?
? ?

理解 Perf:

?

讓我們花一秒鐘來理解?perf?告訴我們的各種性能指標以及它們跟我們代碼的關 系。task-clock?指標告訴我們的 任務花了多少個時鐘周期 。這跟總體的運行時 間不同,因為如果我們的程序花了一秒鐘來運行但是使用了兩個?CPU,那么task-clock?將是?1000(task-clock?的單位是毫秒)。方便的是,perf?會幫我 們計算并在該指標旁邊告訴我們有多少個?CPU?被使用了。這個數字不完全等于?1是因為進程有一段時間依賴于其他子系統的指令(比如分配內存時)。

context-switches?和?CPU-migrations?告訴我們 程序在等待內核操作 (如?I/O操作)完成時,為了讓其他進程得以運行, 被掛起或遷移到另一個?CPU?核心上執 行的次數 。當一個?context-switch?發生時程序的執行會被掛起,讓另一個程序 得以執行。這是一個對時間要求非常精細的任務,也是我們需要盡量避免的,但是 我們對它的發生無能為力。只要進程允許切換,內核就會接手;不過,我們可以做 一些事來抑制內核切換我們的程序。 總的來說,內核會在程序進行?I/O(比如讀取 內存、磁盤或網絡)時將其掛起 。我們在后續章節會看到,我們 可以用異步操作來 確保我們的程序在等待?I/O?時繼續使用?CPU,這會讓我們的進程繼續運行而不被切 換出去 。另外,我們還可以 設置程序的?nice?值來給我們的程序更高的優先級以防 止內核將它切換出去 。類似的, CPU-migrations?會發生在進程被掛起并遷移到 另一個?CPU?上繼續執行的情況,這是為了讓所有的?CPU?都有同樣程度的利用率 。 這可以被認為是一個特別糟糕的進程切換,因為我們的程序不僅被暫時掛起,而且 丟失了?L1?緩存內所有的數據(每個?CPU?都有它自己的?L1?緩存)。

page-fault?是現代?UNIX?內存分配機制的一部分。分配內存時,內核除了告訴 程序一個內存的引用地址以外沒做任何事。但是,之后在這塊內存第一次被使用時, 操作系統會拋出一個缺頁小中斷,這將暫停程序的運行并正確分配內存。這被稱為 延遲分配系統 。雖然這種手段相比以前的內存分配系統是一個很大的優化,缺頁小 中斷本身依然是一個相當昂貴的操作,因為大多數操作都發生在你的程序外部。另 外還有一種缺頁大中斷,發生于當你的程序需要從設備(磁盤、網絡等)上請求還 未被讀取的數據時。這些操作更加昂貴,因為他們不僅中斷了你的程序,還需要讀 取數據所在的設備。這種缺頁不總是影響?CPU?密集的工作,但是,它會給任何需 要讀寫磁盤或網絡的程序帶來痛苦。

?


Numpy 能夠將 數據連續存儲 在內存中并支持數據的矢量操作,在數據處理方面,它是高性能編程的最佳解決方案之一。
Numpy 帶來性能提升的關鍵在于,它使用了高度優化且特殊構建的對象,取代了通用的列表結構來處理數組,由此 減少了內存碎片 ;此外, 自動矢量化 的數學操作使得矩陣計算非常高效。
Numpy 在矢量操作上的 缺陷是一次只能處理一個操作 。例如,當我們做 A B + C 這樣的矢量操作時,先要等待 A B 操作完成,并保存數據在一個臨時矢量中,然后再將這個新的矢量和 C 相加。

編譯器

讓你的代碼運行更快的最簡單的辦法就是讓它做更少的工作。編譯器把代碼編譯成機器碼,是提高性能的關鍵組成部分。

  • Cython ——這是編譯成C最通用的工具,覆蓋了Numpy和普通的Python代碼(需要一些C語言的知識)。
  • Shed Skin —— 一個用于非Numpy代碼的,自動把Python轉換成C的轉換器。
  • Numba —— 一個專用于Numpy代碼的新編譯器。
  • Pythran —— 一個用于Numpy和非numpy代碼的新編譯器。
  • PyPy —— 一個用于非Numpy代碼的,取代常規Python可執行程序的穩定的即時編譯器。
? ?

7.6.1?使用Cython編譯純Python版本開始寫一個擴展編譯模塊的簡單方法涉及?3?個文件。使用我們的?Julia?作為例子,

它們是:

  • 調用它的?Python?代碼(來自前面的?Julia?代碼塊)。

  • 在一個新.pyx?文件中要被編譯的函數。

  • 一個?setup.py,包含了調用?Cython?來制作擴展模塊的指令。

    使用這個方法,setup.py?腳本調用?Cython?把.pyx?文件編譯成一個編譯模塊。在類UNIX?系統上,編譯模塊可能會是一個.so?文件;在?Windows?上應該是一個.pyd(類DLL?的?Python?庫)。

    對于?Julia?的例子,我們會用:

  • julia1.py,構建輸入列表并調用計算函數。

  • cythonfn.pyx,包含了我們能注解的?CPU?密集型函數。

  • setup.py,包含了構建指令。

?
? ?

如果你的?CPU?密集型代碼在頻繁解引用的循環中,嘗試禁止邊界檢 查和外圍檢查。

? ?

一般情況下,可能最消耗?CPU?時間的代碼行是下面這些:??在緊湊的內循環內。
??解引用?list、array?或者?np.array?這些項。
??執行數學運算。

?
cdef 關鍵字 ?
? ?

我們在圖?7-4?中可以看到?while?循環還是相當耗時的(黃色部分)。耗時的調用在 于?Python?對復數?z?的? abs?函數 中。Cython?沒有對復數提供原生的?abs?函數。作 為替代,我們可以提供自己的本地擴展。

?

這個改變有巨大的效果——通過減少在最內循環中?Python?調用的次數,我們大大 降低了函數的運算時間。這個新版本只用?0.25?秒就執行完畢了,具有超過原版本40?倍的速度提升,令人驚嘆。

?

?

?

?

?

密集型任務

  1. I/O 密集型:異步編程?
    • Gevent
    • Tornado
    • Asyncio
  2. CPU 密集型:多核 CPU 進行多進程
    • Multiprocessing
      • multiprocessing.Pool
      • 內存共享
        • multiprocessing.Manager()
        • redis等中間件
        • mmap

集群與現場教訓

  1. 集群帶來的問題:
    1. 機器間信息同步的延遲
    2. 機器間配置與性能的差異
    3. 機器的損耗與維護
    4. 其它難以預料的問題
  2. 集群化解決方案:
    1. Parallel Python
    2. IPython Parallel?
    3. NSQ

?


?

Python貓薦書系列之五:Python高性能編程

Python貓

稍微關心編程語言的使用趨勢的人都知道,最近幾年,國內最火的兩種語言非 Python 與 Go 莫屬,于是,隔三差五就會有人問:這兩種語言誰更厲害/好找工作/高工資……

對于編程語言的爭論,就是猿界的生理周期,每個月都要鬧上一回。到了年末,各類榜單也是特別抓人眼球,鬧得更兇。

其實,它們各有對方所無法比擬的優勢以及用武之地,很多爭論都是沒有必要的。身為一個正在努力學習 Python 的(準)中年程序員,我覺得吧,先把一門語言精進了再說。沒有差勁的語言,只有差勁的程序員,等真的把語言學好了,必定是“山重水復疑無路,柳暗花明又一村”。

鋪墊已了,進入今天的正題,Python 貓薦書系列之五——

Python高性能編程

?

本書適合已入門 Python、還想要進階和提高的讀者閱讀。

所有計算機語言說到底都是在硬件層面的數據操作,所以高性能編程的一個終極目標可以說是“高性能硬件編程”。然而,Python 是一門高度抽象的計算機語言,它的一大優勢是開發團隊的高效,不可否認地存在這樣或那樣的設計缺陷,以及由于開發者的水平而造成的人為的性能缺陷。

本書的一大目的就是通過介紹各種模塊和原理,來促成在快速開發 Python 的同時避免很多性能局限,既減低開發及維護成本,又收獲系統的高效。

1、性能分析是基礎

首先的一個關鍵就是性能分析,借此可以找到性能的瓶頸,使得性能調優做到事半功倍。

性能調優能夠讓你的代碼能夠跑得“足夠快”以及“足夠瘦”。性能分析能夠讓你用最小的代價做出最實用的決定。

書中介紹了幾種性能分析的工具:

(1)基本技術如 IPython 的 %timeit 魔法函數、time.time()、以及一個計時修飾器,使用這些技術來了解語句和函數的行為。

(2)內置工具如 cProfile,了解代碼中哪些函數耗時最長,并用 runsnake 進行可視化。

(3)line_profiler 工具,對選定的函數進行逐行分析,其結果包含每行被調用的次數以及每行花費的時間百分比。

(4)memory_profiler 工具,以圖的形式展示RAM的使用情況隨時間的變化,解釋為什么某個函數占用了比預期更多的 RAM。

(5)Guppy 項目的 heapy 工具,查看 Python 堆中對象的數量以及每個對象的大小,這對于消滅奇怪的內存泄漏特別有用。

(6)dowser 工具,通過Web瀏覽器界面審查一個持續運行的進程中的實時對象。

(7)dis 模塊,查看 CPython 的字節碼,了解基于棧的 Python 虛擬機如何運行。

(8)單元測試,在性能分析時要避免由優化手段帶來的破壞性后果。

作者強調了性能分析的重要性,同時也對如何確保性能分析的成功提了醒,例如, 將測試代碼與主體代碼分離、避免硬件條件的干擾(如在BIOS上禁用了TurboBoost、禁用了操作系統改寫SpeedStep、只使用主電源等) 運行實驗時禁用后臺工具如備份和Dropbox、多次實驗、重啟并重跑實驗來二次驗證結果 ,等等。

性能分析對于高性能編程的作用,就好比復雜度分析對于算法的作用,它本身不是高性能編程的一部分,但卻是最終有效的一種評判標準。

2、數據結構的影響

高性能編程最重要的事情是了解數據結構所能提供的性能保證。

高性能編程的很大一部分是了解你查詢數據的方式,并選擇一個能夠迅速響應這個查詢的數據結構。

書中主要分析了 4 種數據結構:列表和元組就類似于其它編程語言的數組,主要用于存儲具有內在次序的數據;而字典和集合就類似其它編程語言的哈希表/散列集,主要用于存儲無序的數據。

本書在介紹相關內容的時候很克制,所介紹的都是些影響“速度更快、開銷更低”的內容,例如:內置的 Tim 排序算法、列表的 resize 操作帶來的超額分配的開銷、元組的內存滯留(intern機制)帶來的資源優化、散列函數與嗅探函數的工作原理、散列碰撞帶來的麻煩與應對、Python 命名空間的管理,等等。

?

散列碰撞的結果

理解了這些內容,就能更加了解在什么情況下使用什么數據結構,以及如何優化這些數據結構的性能。

另外,關于這 4 種數據結構,書中還得出了一些有趣的結論:對于一個擁有100 000 000個元素的大列表,實際分配的可能是112 500 007個元素;初始化一個列表比初始化一個元組慢5.1 倍;字典或集合默認的最小長度是8(也就是說,即使你只保存3個值,Python仍然會分配 8 個元素)、對于有限大小的字典不存在一個最佳的散列函數。

3、矩陣和矢量計算

矢量計算是計算機工作原理不可或缺的部分,也是在芯片層次上對程序進行加速所必須了解的部分。

然而,原生 Python 并不支持矢量操作,因為 Python 列表存儲的不是實際的數據,而是對實際數據的引用。在矢量和矩陣操作時,這種存儲結構會造成極大的性能下降。比如, grid[5][2] ?中的兩個數字其實是索引值,程序需要根據索引值進行兩次查找,才能獲得實際的數據。

同時,因為數據被分片存儲,我們只能分別對每一片進行傳輸,而不是一次性傳輸整個塊,因此,內存傳輸的開銷也很大。

減少瓶頸最好的方法是讓代碼知道如何分配我們的內存以及如何使用我們的數據進行計算。

Numpy 能夠將數據連續存儲在內存中并支持數據的矢量操作,在數據處理方面,它是高性能編程的最佳解決方案之一。

Numpy 帶來性能提升的關鍵在于,它使用了高度優化且特殊構建的對象,取代通用的列表結構來處理數組,由此減 少了內存碎片 ;此外, 自動矢量化的數學操作使得矩陣計算非常高效

Numpy 在矢量操作上的缺陷是一次只能處理一個操作。例如,當我們做 A * B + C 這樣的矢量操作時,先要等待 A * B 操作完成,并保存數據在一個臨時矢量中,然后再將這個新的矢量和 C 相加。

Python:_第3張圖片

?

Numexpr 模塊可以將矢量表達式編譯成非常高效的代碼,可以 將緩存失效以及臨時變量的數量最小化 。另外,它還能利用多核 CPU 以及 Intel 芯片專用的指令集來將速度最大化。()

書中嘗試了多種優化方法的組合,通過詳細的分析,展示了高性能編程所能帶來的提升效果。

What is NumExpr?

NumExpr is a fast numerical expression evaluator for NumPy. With it, expressions that operate on arrays (like? '3*a+4*b' ) are accelerated and use less memory than doing the same calculation in Python.

In addition, its multi-threaded capabilities can make use of all your cores – which generally results in substantial performance scaling compared to NumPy.

Last but not least, numexpr can make use of Intel’s VML (Vector Math Library, normally integrated in its Math Kernel Library, or MKL). This allows further acceleration of transcendent expressions.

How NumExpr achieves high performance

The main reason why NumExpr achieves better performance than NumPy is that it avoids allocating memory for intermediate results. This results in better cache utilization and reduces memory access in general. Due to this, NumExpr works best with large arrays.

NumExpr parses expressions into its own op-codes that are then used by an integrated computing virtual machine. The array operands are split into small chunks that easily fit in the cache of the CPU and passed to the virtual machine. The virtual machine then applies the operations on each chunk. It’s worth noting that all temporaries and constants in the expression are also chunked. Chunks are distributed among the available cores of the CPU, resulting in highly parallelized code execution.

The result is that NumExpr can get the most of your machine computing capabilities for array-wise computations. Common speed-ups with regard to NumPy are usually between 0.95x (for very simple expressions like? 'a + 1' ) and 4x (for relatively complex ones like? 'a*b-4.1*a > 2.5*b' ), although much higher speed-ups can be achieved for some functions and complex math operations (up to 15x in some cases).

NumExpr performs best on matrices that are too large to fit in L1 CPU cache. In order to get a better idea on the different speed-ups that can be achieved on your platform, run the provided benchmarks.

?
? ?
? ?

4、編譯器

書中提出一個觀點: 讓你的代碼運行更快的最簡單的辦法就是讓它做更少的工作。

編譯器把代碼編譯成機器碼,是提高性能的關鍵組成部分。

Python:_第4張圖片

?

不同的編譯器有什么優勢呢,它們對于性能提升會帶來多少好處呢?書中主要介紹了如下編譯工具:

  • Cython ——這是編譯成C最通用的工具,覆蓋了Numpy和普通的Python代碼(需要一些C語言的知識)。
  • Shed Skin —— 一個用于非Numpy代碼的,自動把Python轉換成C的轉換器。
  • Numba —— 一個專用于Numpy的新編譯器。
  • Pythran —— 一個用于Numpy和非numpy代碼的新編譯器。
  • PyPy —— 一個用于非Numpy代碼的,取代常規Python可執行程序的穩定的即時編譯器。

書中分析了這幾種編譯器的工作原理、優化范圍、以及適用場景等,是不錯的入門介紹。此外,作者還提到了其它的編譯工具,如Theano、Parakeet、PyViennaCL、ViennaCL、Nuitka 與 Pyston 等,它們各有取舍,在不同領域提供了支撐之力。

5、密集型任務

高性能編程的一個改進方向是提高密集型任務的處理效率,而這樣的任務無非兩大類:I/O 密集型與 CPU 密集型。

I/O 密集型任務主要是磁盤讀寫與網絡通信任務,占用較多 I/O 時間,而對 CPU 要求較少;CPU 密集型任務恰恰相反,它們要消耗較多的 CPU 時間,進行大量的復雜的計算,例如計算圓周率與解析視頻等。

改善 I/O 密集型任務的技術是 異步編程 ?,它使得程序在 I/O 阻塞時,并發執行其它任務,并 通過“事件循環”機制 來管理各項任務的運行時機,從而提升程序的執行效率。

書中介紹了 三種異步編程的庫:Gevent、Tornado 和 Asyncio ,對三種模塊的區別做了較多分析。

改善 CPU 密集型任務的主要方法是 利用多核 CPU 進行多進程 的運算。

Multiprocessing 模塊 基于進程和基于線程 的并行處理, 在隊列上共享任務,以及在進程間共享數據 ,是處理CPU密集型任務的重要技術。

書中沒有隱瞞它的局限性:Amdahl 定律揭示的優化限度、適應于單機多核而多機則有其它選擇、全局解釋鎖 GIL 的束縛、以及進程間通信(同步數據和檢查共享數據)的開銷。針對進程間通信問題,書中還分析了多種解決方案,例如 Less Na?ve Pool、Manager、 Redis 、RawValue、MMap 等。

6、集群與現場教訓

集群是一種多服務器運行相同任務的結構,也就是說,集群中的各節點提供相同的服務,其優點是系統擴展容易、具備容災恢復能力。

集群需要克服的挑戰有:機器間信息同步的延遲、機器間配置與性能的差異、機器的損耗與維護、其它難以預料的問題。書中列舉了兩個慘痛的教訓:華爾街公司騎士資本由于軟件升級引入的錯誤,損失4.62億美元;Skype 公司 24 小時全球中斷的嚴重事故。

書中給我們重點介紹了 三個集群化解決方案:Parallel Python、IPython Parallel 和 NSQ。引申也介紹了一些普遍使用的方案,如 Celery、Gearman、PyRes、SQS。

Python:_第5張圖片

?

關于現場教訓,它們不僅僅是一些事故或者故事而已,由成功的公司所總結出來的經驗更是來之不易的智慧。書中單獨用一章內容分享了六篇文章,這些文章出自幾個使用 Python 的公司/大型組織,像是Adaptive Lab、RadimRehurek、Smesh、PyPy 與 Lanyrd ,這些國外組織的一線實踐經驗,應該也能給國內的 Python 社區帶來一些啟示。

7、寫在最后

眾所周知,Python 應用前景大、簡單易學、方便開發與部署,然而與其它編程語言相比,它的性能幾乎總是落于下風。如何解決這個難題呢?本期薦書的書目就是一種回應。

《Python高性能編程》全書從微觀到宏觀對高性能編程的方方面面做了講解,主要包含以下主題:計算機內部結構的背景知識、列表和元組、字典和集合、迭代器和生成器、矩陣和矢量計算、 編譯器、并發、集群和工作隊列 等。這些內容為編寫更快的 Python 指明了答案。

本篇文章主要以梳理書中的內容要點為主,平均而兼顧地理清了全書脈絡(PS:太面面俱到了,但愿不被指責為一篇流水賬的讀書筆記才好……)。我認為,鑒于書中談及的這些話題,它就足以成為我們薦書欄目的一員了。除去某些句段的糟糕翻譯、成書時間比較早(2014年)而造成的過時外,這本書總體質量不錯,可稱為是一份優秀的高性能編程的指引手冊。

關于薦書欄目,我最后多說幾句。本欄目原計劃兩周左右出一篇,但由于其它系列文章花費了我不少時間,而要寫好一篇薦書/書評也特別費勁,最后生生造成了現在兩月一更的尷尬局面……這篇文章是個錯誤的示范,我不該試圖全面通讀與概括其內容的。因此,我決定今后選一些易讀的書目,在寫作上也盡量走短小精悍風,希望能持續地將本欄目運作下去。若你有什么建議(如書目推薦、書評推薦、寫作建議、甚至是投稿),我隨時歡迎,先行致謝啦。


?

?

?

?


?

?


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 色一区二区 | 国产成人精品免费午夜 | 精品视频在线免费观看 | 欧美午夜在线观看理论片 | 亚洲成在人 | 日韩视频免费在线播放 | 国产在线视频精品视频免费看 | 欧美爱爱视频网站 | 日本一级www乱码在线 | 国产精品98视频全部国产 | 欧美激情第一区 | 日日操综合 | 国产一级毛片夜一级毛片 | 成人亚洲欧美 | 欧美日韩亚洲一区二区 | 亚洲成人网在线 | 亚洲欧美一区二区三区麻豆 | 亚洲日本在线观看视频 | 国产欧美久久一区二区 | 美女视频黄是免费的 | 国产精品久久久久国产精品三级 | 日韩毛片基地一区二区三区 | 一区二区三区日韩 | 五月激情综合网 | 日韩亚射 | 亚洲国产二区三区 | 99re这里只有精品99 | 久久是免费只精品热在线 | 久久天天躁夜夜躁狠狠躁2015 | 国产视频精品视频 | 久久无码精品一区二区三区 | 天天操夜夜操天天操 | 久久综合久久网 | 伊人精品网 | 亚洲夜夜操 | 国产特黄一级毛片特黄 | 日本成人不卡 | 九九视频免费精品视频免费 | 国产一区视频在线 | 美女福利视频一区二区 | 精品国产一二三区在线影院 |