简体   繁体   English

jupyterLab %%timeit 与 python timeit 不一致

[英]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" .正如您自己注意到的那样, “函数定义需要更多时间”

Why you saw a difference为什么你看到了不同

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 your classmate didn't see a difference为什么你的同学没有看到不同

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.但是,如果我在一个单元格中定义函数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)

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM