《構建之法》教學筆記——Python中的效能分析與幾個問題

《構建之法:現代軟件工程》中第2章對效能分析進行了介紹,基於的工具是VSTS。因爲我教授的學生中只有部分同窗選修了C#,若採用書中例子講解,學生可能理解起來比較困難。不過全部這些學生都學習過Python,所以我就基於書中對效能分析的介紹,結合Python效能分析工具的文檔以及互聯網上的博客,準備了一份關於效能分析的講座,內容以下。html

什麼是效能分析?

這部分的講解和書中相似。不過有兩個問題:python

  1. 爲何是效能不是效率,二者之間究竟有什麼區別?這是學生提出的問題。我的以爲兩者之間的差異不大。
  2. 效能分析是否包括內存優化?也就是程序的運行須要更少的內存。若是不包括的話,是基於什麼樣的考慮呢?

效能分析的目標

VSTS提供了方便的效能分析工具,讓咱們能很快地找到程序的效能瓶頸,從而能有的放矢,改進程序。——《構建之法:現代軟件工程》編程

很是贊同這句話,而且認爲效能分析的目標其實就是作到 有的放矢 。時間是很寶貴的資源,若是不通過分析立馬開始進行程序效能的優化提高,可能花了1天時間所得到的優化效果還抵不上通過效能分析後改動兩三行代碼代碼所得到的優化效果。app

效能分析的方法

1. 抽樣(Sampling)
根據《構建之法》的描述,抽樣是指效能分析工具會時不時看一看這個程序運行在哪一個函數內,並記錄下來。程序結束後,效能分析工具就能得出一個關於程序運行時間的大體印象。編程語言

2. 代碼注入(Instrumentation)
根據《構建之法》的描述,代碼注入是指將檢測代碼加入到每個函數中,檢測代碼會記錄程序運行的一舉一動,程序的各個效能數據均可以被精確的測量。ide

3. 抽樣與代碼注入的優缺點比較
根據《構建之法》的描述,我總結出以下表格。函數

方法 是否須要改動程序 運行速度 是否能夠找到程序瓶頸 可否得出精確數據 可否準確表示調用關係樹 是否影響程序運行
抽樣 較快 能夠
代碼注入 相對抽樣方法較慢 能夠

4. Python中的效能分析方法工具

通常的作法是,先用抽樣方法找到效能瓶頸所在,而後對特定的模塊用代碼注入的方法進行詳細分析。——《構建之法:現代軟件工程》post

在Python中的效能分析方法和《構建之法》中描述的有些不同。 在Python中進行效能分析用到的工具是cProfile。在Python自帶的關於cProfile的幫助文檔中,有一段是介紹肯定性效能分析(Deterministic Profiling)的,根據文中所述,我的理解,這應該是指代碼注入方法。其中的一段描述很是重要,且給我帶來了一個很大的疑惑。性能

In Python, since there is an interpreter active during execution, the presence of instrumented code is not required to do deterministic profiling. Python automatically provides a hook (optional callback) for each event. In addition, the interpreted nature of Python tends to add so much overhead to execution, that deterministic profiling tends to only add small processing overhead in typical applications. The result is that deterministic profiling is not that expensive, yet provides extensive run time statistics about the execution of a Python program. ——《Python 2.7.5 documentation》

全文大體意思以下(非專業翻譯):

在Python中,因爲程序執行期間解釋器是處於激活狀態的,所以注入程序中的代碼是無需進行肯定性分析的。Python中的每一個事件都自帶鉤子(可選回調函數)。另外,Python解釋性編程語言的本質趨向於給程序添加許多執行開銷以至於肯定性分析趨向於在典型應用中僅僅添加少許的處理開銷。結果就是,肯定性分析給Python程序執行提供普遍的運行時統計,卻沒有那麼高昂的代價。

也就是說,在Python中代碼注入方法並不會產生太大的開銷,緣由是由於Python是一種解釋型編程語言。解釋型的編程語言有一些特性使得代碼注入方式的效能分析並不會增長Python程序的運行時間。個人困惑是: 到底是解釋型編程語言的什麼特性致使代碼注入方式的效能分析不會增長Python程序的運行時間?

鑑於以上狀況,在Python中進行效能分析,直接使用代碼注入方法便可。使用的工具就是cProfile。

cProfile的使用方法

關於cProfile的基本使用方法,已經有很多博客解釋說明,能夠參考這個:應用python的性能測量工具cProfile。還有官方文檔:26.4. The Python Profilers

Python中使用cProfile進行效能分析的示例

模仿《構建之法》中統計詞頻的程序,我用Python寫了一個能夠進行詞頻統計的程序(未通過全面測試)。程序代碼以下:

from string import punctuation
def process_file(dst):
    try:
        f = open(dst)
    except IOError, s:
        print s
        return None
    try:
        bvffer = f.read()
    except:
        print "Read File Error!"
        return None
    f.close()
    return bvffer

def process_buffer(bvffer):
    if bvffer:
        word_freq = {}
        for item in bvffer.strip().split():
            word = item.strip(punctuation+' ')
            if word in word_freq.keys():
                word_freq[word] += 1
            else:
                word_freq[word] = 1
        return word_freq

def output_result(word_freq):
    if word_freq:
        sorted_word_freq = sorted(word_freq.items(), key=lambda v: v[1], reverse=True)
        for item in sorted_word_freq[:10]:
            print item

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument('dst')
    args = parser.parse_args()
    dst = args.dst
    bvffer = process_file(dst)
    word_freq = process_buffer(bvffer)
    output_result(word_freq)

而後進入命令行並進入程序所在目錄後輸入如下命令:

python -m cProfile word_freq.py semeval-sts/2016/postediting.test.txt

其中 semeval-sts/2016/postediting.test.txt 是一個句子語義類似度計算語料庫, 大小775K過小的話看不出優化效果 。以後會獲得以下分析結果:
1
2
2
4
如圖1所示, 總共有283304次函數調用,程序總共耗時29.809秒 。如圖3所示, 字典的keys方法被調用的次數爲136066次,花費的總時間爲15.285秒 。仔細觀察代碼發現函數process_buffer函數中有一行代碼:

if word in word_freq.keys():

該代碼在for循環中,有多少單詞,這個循環就會執行多少遍,每次進行條件判斷的時候都要執行一次字典的keys方法,因此耗時不少。因而把keys去除,該行代碼變爲:

if word in word_freq:

再次進行效能分析,進入命令行,輸入如上同樣的命令:

python -m cProfile word_freq.py semeval-sts/2016/postediting.test.txt

首先,感受程序瞬間就結束了,不像上面的程序運行了一段時間才顯示結果。其次程序的函數調用次數和總運行時間減小了。以下圖:
1
程序總共有147238次函數調用,耗時0.152秒 。 通過以上過程咱們實現了對程序的優化,只需簡單的去除一些代碼就能夠,並且效果很是顯著。這就是效能分析的意義!

相關文章
相關標籤/搜索