簡體   English   中英

jupyterLab %%timeit 與 python timeit 不一致

[英]jupyterLab %%timeit not consistent with python timeit

我想測試類型提示是否會影響代碼運行的時間。 它可能會增加一點時間,因為編譯器必須忽略它,這需要時間,但我想看看這是多么微不足道。

為此,我在 jupyterLab 上執行了此操作: 在此處輸入圖像描述

但是,我的一位同學在沒有 jupyterLab 的情況下嘗試了這個,發現: 在此處輸入圖像描述

有人對為什么會發生這種情況有解釋嗎?

使用的功能:

def func(a, b):
    return a + b
func(6, 7)    

和:

def func(a: int, b: int) -> int:
    return a + b
func(6, 7)

正如您自己注意到的那樣, “函數定義需要更多時間”

為什么你看到了不同

Pythengers,拆開!

import dis

dis.dis('''
def func(a, b):
    return a + b
''')

輸出(使用 Python 3.10.4):

  2           0 LOAD_CONST               0 (<code object func at 0x00000243938989D0, file "<dis>", line 2>)
              2 LOAD_CONST               1 ('func')
              4 MAKE_FUNCTION            0
              6 STORE_NAME               0 (func)
              8 LOAD_CONST               2 (None)
             10 RETURN_VALUE

Disassembly of <code object func at 0x00000243938989D0, file "<dis>", line 2>:
  3           0 LOAD_FAST                0 (a)
              2 LOAD_FAST                1 (b)
              4 BINARY_ADD
              6 RETURN_VALUE

加載已編譯的代碼對象,從中創建一個函數對象,並將其分配給名稱func

使用帶注釋的版本,我們得到的是:

  2           0 LOAD_CONST               0 ('a')
              2 LOAD_NAME                0 (int)
              4 LOAD_CONST               1 ('b')
              6 LOAD_NAME                0 (int)
              8 LOAD_CONST               2 ('return')
             10 LOAD_NAME                0 (int)
             12 BUILD_TUPLE              6
             14 LOAD_CONST               3 (<code object func at 0x00000243940F0A80, file "<dis>", line 2>)
             16 LOAD_CONST               4 ('func')
             18 MAKE_FUNCTION            4 (annotations)
             20 STORE_NAME               1 (func)
             22 LOAD_CONST               5 (None)
             24 RETURN_VALUE

Disassembly of <code object func at 0x00000243940F0A80, file "<dis>", line 2>:
  3           0 LOAD_FAST                0 (a)
              2 LOAD_FAST                1 (b)
              4 BINARY_ADD
              6 RETURN_VALUE

因此,當被執行時,會為注釋完成一些額外的工作,這會花費額外的時間。

為什么你的同學沒有看到不同

為什么這對你同學的測量方式沒有影響? 因為他們並沒有真正衡量它。 嘗試在a.py中打印一些東西:

print('importing a.py ...')
def func(a, b):
    return a + b
func(6, 7)

然后再試一次:

> python -m timeit -n 10000000 "import a"
importing a.py ...
10000000 loops, best of 5: 370 nsec per loop
>

一千萬次循環,但我們的消息只打印了一次 因為 Python 緩存導入並且不會重新導入已經導入的內容。 因此,雖然這確實衡量了函數定義的單次執行,但在執行一千萬個 import 語句中這完全是微不足道的。 真的,你的同學沒有測量他們打算測量的代碼,而是測量了導入語句(幾乎所有這些都被識別為重新導入然后被忽略)。

我認為諸如%%timeit之類的魔術函數會導致筆記本解釋單元格。 在命令行和筆記本中運行你的代碼,我得到了與你相似的結果。

但是,如果我在一個單元格中定義函數afuncbfunc ,然后在不同的單元格中分別定義%%timeit ,則結果再次彼此一致。

[1]
def afunc(a: int, b: int) -> int:
    return a + b

def bfunc(a, b):
    return a + b
[2]
%%timeit
afunc(1,2)
40.9 ns ± 0.131 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
[3]
%%timeit
bfunc(1,2)
42.9 ns ± 0.0417 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

沒有顯着差異,而

[1]
%%timeit
def afunc(a: int, b: int) -> int:
    return a + b
afunc(1,2)

145 ns ± 0.765 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
[2]
%%timeit
def bfunc(a, b):
    return a + b
bfunc(1,2)
71.2 ns ± 0.341 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

給我們帶來了巨大的不同

我們可以更進一步,這樣做:

[1]
%%timeit
def afunc(a: int, b: int) -> int:
    return a + b
111 ns ± 0.358 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
[2]
%%timeit
def bfunc(a, b):
    return a + b
32.6 ns ± 0.255 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

這里很明顯,解釋是測量的,因為沒有執行用戶函數調用,只是函數本身的定義。 我們觀察到相同的差異,減去運行任一功能所需的約 40ns。

控制台中更簡單的代碼似乎證實了這不是 JupyterLab 特有的,盡管這段代碼更容易滿足exec的要求:

C:\Users\Ben>python -m timeit "exec('a: int = 1')"
50000 loops, best of 5: 4.91 usec per loop

C:\Users\Ben>python -m timeit "exec('a = 1')"
50000 loops, best of 5: 4.02 usec per loop

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM