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


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

什么是效能分析?

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

  1. 為什么是效能不是效率,兩者之間究竟有什么區別?這是學生提出的問題。個人覺得二者之間的差別不大。
  2. 效能分析是否包括內存優化?也就是程序的運行需要更少的內存。如果不包括的話,是基於什么樣的考慮呢?

效能分析的目標

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

非常贊同這句話,並且認為效能分析的目標其實就是做到 有的放矢 。時間是很寶貴的資源,如果不經過分析立馬開始進行程序效能的優化提升,可能花了1天時間所獲得的優化效果還抵不上經過效能分析后改動兩三行代碼代碼所獲得的優化效果。

效能分析的方法

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

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

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

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

4. Python中的效能分析方法

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

在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秒
經過以上過程我們實現了對程序的優化,只需簡單的去除一些代碼就可以,而且效果非常顯著。這就是效能分析的意義!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM