首页 > Python > 为什么 Python 代码在函数中运行得更快?

为什么 Python 代码在函数中运行得更快?

上一篇 下一篇

def main():
    for i in xrange(10**8):
        pass
main()

这段代码在 Python 中运行(注意:计时是通过 Linux 中 BASH 中的时间函数完成的。

real    0m1.841s
user    0m1.828s
sys     0m0.012s

但是,如果 for 循环未放置在函数中,

for i in xrange(10**8):
    pass

然后它运行更长的时间:

real    0m4.543s
user    0m4.524s
sys     0m0.012s

这是为什么呢?

分割线

网友回答:

在函数内部,字节码为:

  2           0 SETUP_LOOP              20 (to 23)
              3 LOAD_GLOBAL              0 (xrange)
              6 LOAD_CONST               3 (100000000)
              9 CALL_FUNCTION            1
             12 GET_ITER            
        >>   13 FOR_ITER                 6 (to 22)
             16 STORE_FAST               0 (i)

  3          19 JUMP_ABSOLUTE           13
        >>   22 POP_BLOCK           
        >>   23 LOAD_CONST               0 (None)
             26 RETURN_VALUE        

在顶层,字节码为:

  1           0 SETUP_LOOP              20 (to 23)
              3 LOAD_NAME                0 (xrange)
              6 LOAD_CONST               3 (100000000)
              9 CALL_FUNCTION            1
             12 GET_ITER            
        >>   13 FOR_ITER                 6 (to 22)
             16 STORE_NAME               1 (i)

  2          19 JUMP_ABSOLUTE           13
        >>   22 POP_BLOCK           
        >>   23 LOAD_CONST               2 (None)
             26 RETURN_VALUE        

不同之处在于 比 快 (!)。这是因为在函数中,它是局部的,但在顶层它是全局的。STORE_FASTSTORE_NAMEi

要检查字节码,请使用该模块。我可以直接反汇编函数,但要反汇编顶级代码,我必须使用内置代码。discompile

分割线

网友回答:

您可能会问为什么存储局部变量比全局变量更快。这是CPython的实现细节。

请记住,CPython被编译为字节码,解释器运行。编译函数时,局部变量存储在固定大小的数组(不是 )中,变量名称分配给索引。这是可能的,因为您无法动态地向函数添加局部变量。然后检索局部变量实际上是对列表的指针查找和 refcount 的增加,这是微不足道的。dictPyObject

将其与全局查找 () 进行对比,全局查找是涉及哈希等的真正搜索。顺便说一下,这就是为什么你需要指定你是否希望它是全局的:如果你曾经赋值给作用域内的变量,编译器将发出 s 来访问它,除非你告诉它不要这样做。LOAD_GLOBALdictglobal iSTORE_FAST

顺便说一下,全局查找仍然非常优化。属性查找非常慢!foo.bar

这是关于局部可变效率的小插图。

分割线

网友回答:

除了局部/全局变量存储时间外,操作码预测使函数更快。

正如其他答案所解释的那样,该函数在循环中使用操作码。下面是函数循环的字节码:STORE_FAST

    >>   13 FOR_ITER                 6 (to 22)   # get next value from iterator
         16 STORE_FAST               0 (x)       # set local variable
         19 JUMP_ABSOLUTE           13           # back to FOR_ITER

通常,当程序运行时,Python 会一个接一个地执行每个操作码,跟踪堆栈并在执行每个操作码后对堆栈帧进行其他检查。操作码预测意味着在某些情况下,Python能够直接跳转到下一个操作码,从而避免了一些开销。

在这种情况下,每次 Python 看到(循环的顶部)时,它都会“预测”它是它必须执行的下一个操作码。然后,Python 会查看下一个操作码,如果预测正确,它会直接跳转到 。这具有将两个操作码压缩为单个操作码的效果。FOR_ITERSTORE_FASTSTORE_FAST

另一方面,操作码在全局级别的循环中使用。Python 在看到这个操作码时*不会*做出类似的预测。相反,它必须回到评估循环的顶部,这对循环的执行速度有明显的影响。STORE_NAME

为了提供有关此优化的更多技术细节,以下是文件(Python虚拟机的“引擎”)中的引用:ceval.c

一些操作码倾向于成对出现,因此可以在运行第一个代码时
预测第二个代码。例如,
后面通常跟着 。FOR_ITER后面通常
着STORE_FAST
或.
GET_ITERFOR_ITERUNPACK_SEQUENCE

验证预测需要根据常数对寄存器
变量进行单次高速测试。如果配对良好,则
处理器自身的内部分支预测很有可能
成功,从而导致到
下一个操作码的开销几乎为零。成功的预测可以节省通过评估循环
的行程,包括其两个不可预测的分支,即测试和
开关案例。结合处理器的内部分支预测,成功具有使两个操作码运行的效果,就好像
它们是单个新操作码一样,
主体组合在一起。
HAS_ARGPREDICT

我们可以在操作码的源代码中看到预测的确切位置:FOR_ITERSTORE_FAST

case FOR_ITER:                         // the FOR_ITER opcode case
    v = TOP();
    x = (*v->ob_type->tp_iternext)(v); // x is the next value from iterator
    if (x != NULL) {                     
        PUSH(x);                       // put x on top of the stack
        PREDICT(STORE_FAST);           // predict STORE_FAST will follow - success!
        PREDICT(UNPACK_SEQUENCE);      // this and everything below is skipped
        continue;
    }
    // error-checking and more code for when the iterator ends normally                                     

该函数扩展为即我们只是跳到预测操作码的开头。在这种情况下,我们跳到这里:PREDICTif (*next_instr == op) goto PRED_##op

PREDICTED_WITH_ARG(STORE_FAST);
case STORE_FAST:
    v = POP();                     // pop x back off the stack
    SETLOCAL(oparg, v);            // set it as the new local variable
    goto fast_next_opcode;

现在设置了局部变量,下一个操作码可以执行了。Python 继续通过可迭代对象,直到它到达终点,每次都做出成功的预测。

Python wiki 页面提供了有关 CPython 虚拟机如何工作的更多信息。

模板简介:该模板名称为【为什么 Python 代码在函数中运行得更快?】,大小是暂无信息,文档格式为.编程语言,推荐使用Sublime/Dreamweaver/HBuilder打开,作品中的图片,文字等数据均可修改,图片请在作品中选中图片替换即可,文字修改直接点击文字修改即可,您也可以新增或修改作品中的内容,该模板来自用户分享,如有侵权行为请联系网站客服处理。欢迎来懒人模板【Python】栏目查找您需要的精美模板。

相关搜索
  • 下载密码 lanrenmb
  • 下载次数 247次
  • 使用软件 Sublime/Dreamweaver/HBuilder
  • 文件格式 编程语言
  • 文件大小 暂无信息
  • 上传时间 04-19
  • 作者 网友投稿
  • 肖像权 人物画像及字体仅供参考
栏目分类 更多 >
热门推荐 更多 >
html5 微信模板 响应式 企业网站 微信图片 单页式简历模板 自适应 微信公众平台 微信文章 微信素材
您可能会喜欢的其他模板