python 使用 Dis 模組進行程式碼效能剖析

pythondict發表於2020-05-15

Python程式碼在執行的時候,會被編譯為Python位元組碼,再由Python虛擬機器執行Python位元組碼。有時候就我們執行python檔案的時候會生成一個pyc檔案,這個pyc檔案即用於儲存Python位元組碼指令,而這些位元組碼是一種類似於彙編指令的中間語言,但是每個位元組碼對應的不是機器指令,而是一段C程式碼。

而Dis模組,就是用於檢視這些位元組碼的執行軌跡,因此我們可以用Dis模組判斷兩個函式的記憶體佔用誰會更大,誰會更消耗CPU效能,不僅如此,通過指令,我們還可以知道Python中一些內建函式、變數的取值過程、執行邏輯,對於我們程式碼效能並優化程式碼很有幫助。

下面將通過兩個例子,來介紹Dis模組的使用。

1.為什麼下面第一個函式比第二個函式耗得記憶體更少?

def test1(a):
    if 0 < a and a < 1:
        return 1
    return 0

def test2(a):
    if 0 < a < 1:
        return 1
    return 0

一般人是比較難直接看出來的,但是我們使用Dis模組卻能很容易找到答案:

import dis
def test1(a):
    if 0 < a and a < 1:
        return 1
    return 0
def test2(a):
    if 0 < a < 1:
        return 1
    return 0
dis.dis(test1)
print('*'*50)
dis.dis(test2)

結果:

dis結果

Dis的結果其實很容易閱讀:

第一列:對應的原始碼行數。
第二列:對應的記憶體位元組碼的索引位置。
在第一列和第二列之間的 >> 號表示跳轉的目標
第三列:內部機器程式碼的操作。
第四列:指令引數。
第五列:實際引數。

兩個函式的dis分析用*號隔開了,大家可以清晰地看到兩個函式之間的語句區別。第二個函式的位元組碼索引最大到了30,而第一個函式的位元組碼索引最大僅到了22,因此,第一個函式耗得記憶體比第二個函式少。

而且,在第一列和第二列之間的 >> 號表示跳轉的目標,大家可以看第二個函式第四列的 18,表示其跳轉到了索引為18的指令,也就是ROT_TWO。第二個函式的跳轉也比第一個函式多,這也可能導致其在某種特殊情況下的效率可能會比第一個函式低。

2.為什麼Python2中,while True 比 while 1慢?

while 1:
    pass

while True:
    pass

可以通過在命令中使用dis進行分析:

可以看到,while 1 在第二行是直接JUMP_ABSOLUTE,因此相比於While True 少了LOAD_NAME 和 POP_JUMP_IF_FALSE。這是因為True在Python2中不是一個關鍵字,而是一個內建變數,因此每次Python都會用LOAD_NAME去檢查(POP_JUMP_IF_FALSE)True的值。這就是為什麼While True 比while 1慢的原因。

到了Python3,True變成了關鍵字,就沒有這個問題了:

Python 3 針對 Python 2 做了非常多的替換,這也是為什麼它不相容 Python 2 的原因之一,差別太大了。因此,建議各位初學者直接上手 Python 3 進行學習,而非 Python 2.

希望以上兩個Dis模組的使用例子能給大家帶來一點靈感,分析一段Python程式碼的深層次效能問題雖然比較費時費力,但是一旦你分析到了深層次的效能原因,將能累積不少深層次的技術上的知識,寫出更漂亮的程式碼。

我們的文章到此就結束啦,如果你希望我們今天的Python 教程,請持續關注我們,如果對你有幫助,麻煩在下面點一個贊/在看哦!有任何問題都可以在下方留言區留言,我們都會耐心解答的!


​Python實用寶典 (pythondict.com)
不只是一個寶典
歡迎關注公眾號:Python實用寶典

原文來自Python實用寶典:python Dis模組程式碼效能

本作品採用《CC 協議》,轉載必須註明作者和本文連結

Python實用寶典, pythondict.com

相關文章