用line_profiler做Python profile. 給Python做性能分析,程序的瓶頸在哪裏,每行代碼運行多長時間?爲什麼 line_profiler 比 cProfile好?

摘要:本篇博客簡單講解如何給Python程序做profile。profie很有用的,它可以分解程序各個部分花費了多少時間。



1 line_profiler

在linux下我用time這個命令可以查看程序運行的總時間,user mode時間和kernel mode時間。但是如果想更進一步,查看程序內部哪一行命令花費多少時間,或者查找程序的瓶頸部分,就得用 line_profiler 這個包了。這對於優化程序很有幫助。

1.1 安裝

官方安裝方法:

pip install line_profiler

但是我用這個方法安裝失敗了。。。我是Anaconda環境,於是使用conda安裝,成功了:

conda install line_profiler

2 使用

分爲三個步驟:

  1. import line_profiler
  2. 給函數加上 @profile 這個裝飾器
  3. 在命令行裏面輸入 kernprof -v -l code.py

這裏code.py 是要做profile的程序


3 Talk is cheap, show me the code.

代碼:

	import line_profiler
	
    // ...
    
	@profile
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        ''' s -- O(N)
            words -- O(M)
        '''
        from collections import defaultdict
        if len(words) == 0 or len(s) == 0:
            return []

        answer = []
        length = len(words[0])
        s_pro = Solution.preprocess_s(s, length)
        word_num = len(words)
        word_dict = defaultdict(int)
        for w in words:
            word_dict[w] += 1
        for i in range(0, len(s) - int(length*word_num) + 1):
            words_tmp = Solution.get_words(s_pro, i, length, word_num)
            if Solution.match_words(words_tmp, word_dict):
                answer.append(i)
        return answer

輸出:

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
   149                                               @profile
   150                                               def findSubstring(self, s: str, words: List[str]) -> List[int]:
   151                                                   ''' s -- O(N)
   152                                                       words -- O(M)
   153                                                   '''
   154         1         11.0     11.0      0.0          from collections import defaultdict
   155                                           
   156         1          1.0      1.0      0.0          if len(words) == 0 or len(s) == 0:
   157                                                       return []
   158                                           
   159         1          0.0      0.0      0.0          answer = []
   160         1          1.0      1.0      0.0          length = len(words[0])
   161         1       4376.0   4376.0      0.2          s_pro = Solution.preprocess_s(s, length)
   162         1          0.0      0.0      0.0          word_num = len(words)
   163         1          2.0      2.0      0.0          word_dict = defaultdict(int)
   164       201         77.0      0.4      0.0          for w in words:
   165       200         96.0      0.5      0.0              word_dict[w] += 1
   166      9602       4482.0      0.5      0.2          for i in range(0, len(s) - int(length*word_num) + 1):
   167      9601    1079034.0    112.4     52.4              words_tmp = Solution.get_words(s_pro, i, length, word_num)
   168      9601     972198.0    101.3     47.2              if Solution.match_words(words_tmp, word_dict):
   169                                                           answer.append(i)
   170         1          0.0      0.0      0.0          return answer

3.1 分析

屬性 解釋
line # 行數
Hits 這一行運行了多少次
Time 在這一行上總共花費了多少時間
Per Hit Time除以Hits
% Time 時間佔比
Line Contents 這一行的代碼內容

可以看出,代碼第167行時間佔比是52.4%,第168行時間佔比47.2%。


4 討論

  • 使用line_profiler會使程序運行變慢,時間大約翻倍。但是沒關係,line_profiler的的核心目的是找到程序的瓶頸部分,也就是時間佔比 %Time。如果每一行都變慢一樣的比例的話,不影響時間佔比。
  • line_profiler不支持多進程,如果程序中使用了 joblib 或者 subprocess 的話,就不能用 line_profiler。
  • line_profiler這個包是一個叫做Robert Kern的大牛寫的,所以命令叫做kernprof。
  • cProfile 也可以做profile。我不常使用這個包,因爲我不喜歡這個包的輸出結果。line_profiler可以輸出每行的時間,這個我喜歡。
  • 上述都是對時間做profile。對內存做profile呢?使用這個包:memory-profiler

The EndThe\ End

喜歡記得點贊,收藏,甚至打賞喲~


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章