[英]jupyterLab %%timeit not consistent with python timeit
I wanted to test if type hinting had an influence on how long did the code take to run.我想测试类型提示是否会影响代码运行的时间。 It probably would add a tiny bit of time because the compiler has to ignore it, and that takes time but I wanted to see how unsignificant this was.
它可能会增加一点时间,因为编译器必须忽略它,这需要时间,但我想看看这是多么微不足道。
To do this, I executed this on jupyterLab:为此,我在 jupyterLab 上执行了此操作:
However, one of my classmates tried this without jupyterLab and found this:但是,我的一位同学在没有 jupyterLab 的情况下尝试了这个,发现:
Does someone have an explanation as to why this would be happening?有人对为什么会发生这种情况有解释吗?
Functions used:使用的功能:
def func(a, b):
return a + b
func(6, 7)
and:和:
def func(a: int, b: int) -> int:
return a + b
func(6, 7)
As you noticed yourself, "The function definition is what takes more time" .正如您自己注意到的那样, “函数定义需要更多时间” 。
Pythengers, disassemble! Pythengers,拆开!
import dis
dis.dis('''
def func(a, b):
return a + b
''')
Output (using Python 3.10.4):输出(使用 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
Loads the compiled code object, makes a function object from it, and assigns it to the name func
.加载已编译的代码对象,从中创建一个函数对象,并将其分配给名称
func
。
With the annotated version, we instead get this:使用带注释的版本,我们得到的是:
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
So when that gets executed, some additional stuff gets done for the annotations, which costs additional time.因此,当它被执行时,会为注释完成一些额外的工作,这会花费额外的时间。
Why does it not make a difference for your classmate's way of measuring?为什么这对你同学的测量方式没有影响? Because they're not really measuring it.
因为他们并没有真正衡量它。 Try printing something in
a.py
:尝试在
a.py
中打印一些东西:
print('importing a.py ...')
def func(a, b):
return a + b
func(6, 7)
Then try it again:然后再试一次:
> python -m timeit -n 10000000 "import a"
importing a.py ...
10000000 loops, best of 5: 370 nsec per loop
>
Ten million loops, but our message only got printed once .一千万次循环,但我们的消息只打印了一次。 Because Python caches imports and doesn't reimport what's already imported.
因为 Python 缓存导入并且不会重新导入已经导入的内容。 So while this does measure a single execution of the function definition, that's utterly insignificant among executing ten million import statements.
因此,虽然这确实衡量了函数定义的单次执行,但在执行一千万个 import 语句中这完全是微不足道的。 Really your classmate didn't measure the code they intended to measure, but measured import statements (almost all of which got recognized as reimports and then ignored).
真的,你的同学没有测量他们打算测量的代码,而是测量了导入语句(几乎所有这些都被识别为重新导入然后被忽略)。
I think magic functions such as %%timeit
cause the notebook to interpret the cell.我认为诸如
%%timeit
之类的魔术函数会导致笔记本解释单元格。 Running your code in both a command line and a notebook, I get similar results to yours.在命令行和笔记本中运行你的代码,我得到了与你相似的结果。
However, if I define the functions afunc
and bfunc
in one cell and then %%timeit
each in a different cell, results are consistent with each other again.但是,如果我在一个单元格中定义函数
afunc
和bfunc
,然后在不同的单元格中分别定义%%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)
No significant difference, whereas没有显着差异,而
[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)
Gives us a massive difference给我们带来了巨大的不同
We can take it further and do this:我们可以更进一步,这样做:
[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)
Here it becomes obvious that interpretation is what is being measured as no user function call is being executed, just the definition of the function itself.这里很明显,解释是测量的,因为没有执行用户函数调用,只是函数本身的定义。 We observe the same discrepancy, minus the ~40ns to run either function.
我们观察到相同的差异,减去运行任一功能所需的约 40ns。
Simpler code in a console seems to confirm that this isn't specific to JupyterLab, although this code is much simpler to fit exec
's requirements:控制台中更简单的代码似乎证实了这不是 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.