AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 79275441
Accepted
PkDrew
PkDrew
Asked: 2024-12-12 22:15:09 +0800 CST2024-12-12 22:15:09 +0800 CST 2024-12-12 22:15:09 +0800 CST

程序员在编写Python代码时是否需要手动实现诸如循环展开等优化?

  • 772

我最近正在学习一些 HPC 主题,并且了解到现代 C/C++ 编译器能够检测有权进行优化的地方,并使用相应的技术(如 SIMD、循环展开等)进行优化,尤其是在标志下-O3,在运行时性能与编译时间和目标文件大小之间进行权衡。

然后我立即想到 CPython 是即时解释和执行的,所以我认为它无法承担这些编译器功能,因为它的编译时间相当于运行时,所以我做了下面的一个玩具实验:

import time, random

n = 512
A = [[random.random() for _ in range(n)] for _ in range(n)]
B = [[random.random() for _ in range(n)] for _ in range(n)]
C = [[0] * n for _ in range(n)]

def matMul( A, B, C ):
    """ C = A .* B """
    for i in range(0, n - 4, 4):
        for j in range(0, n - 4, 4):
            for k in range(0, n - 4, 4):
                C[i][j] = A[i][k] * B[k][j]
                C[i + 1][j + 1] = A[i + 1][k + 1] * B[k + 1][j + 1]
                C[i + 2][j + 2] = A[i + 2][k + 2] * B[k + 2][j + 2]
                C[i + 3][j + 3] = A[i + 3][k + 3] * B[k + 3][j + 3]
                C[i + 4][j + 4] = A[i + 4][k + 4] * B[k + 4][j + 4]
    # return C

start = time.time()
matMul( A, B, C )
end = time.time()

print( f"Elapsed {end - start}" )

循环展开后,程序在 3 秒内完成,如果不展开,则需要将近 20 秒。

这是否意味着在编写 Python 代码时需要注意并手动实现这些 opt 技术?或者 Python 是否在任何特殊设置下提供优化?

python
  • 2 2 个回答
  • 102 Views

2 个回答

  • Voted
  1. Best Answer
    Josh Kelley
    2024-12-12T22:36:01+08:002024-12-12T22:36:01+08:00

    循环展开很有用,因为它可以(1)减少管理循环所花费的开销;(2)在汇编级别,它通过消除分支惩罚、保持指令流水线满载等方式让处理器运行得更快。

    (2) 并不适用于像 Python 这样的解释型语言实现 - 它已经在汇编级别进行了大量分支和决策。它可能会通过 (1) 为你带来好处,但我的直觉是,与解释器开销相比,这段时间往往微不足道。性能优化的黄金法则是首先测量并确认瓶颈是否在你认为的位置。

    顺便说一句,你的代码有一个错误:

                    C[i][j] = A[i][k] + B[k][j]
                    C[i + 1][j + 1] = A[i + 1][k + 1] + B[k + 1][j + 1]
                    C[i + 2][j + 2] = A[i + 2][k + 2] + B[k + 2][j + 2]
                    C[i + 3][j + 3] = A[i + 3][k + 3] + B[k + 3][j + 3]
                    C[i + 4][j + 4] = A[i + 4][k + 4] + B[k + 4][j + 4]
    

    它处理单元格 (0, 0)、(1, 1)、(2, 2)、(3, 3) 和 (4, 4)(尽管 (4, 4) 也将在下一次迭代中处理),但不处理 (0, 1)、(0, 2)、(1, 0)...(这是性能优化黄金法则的另一个原因:优化不需要的代码很容易犯错误。)

    正如@Mat所说,Python 的标准方法是使用NumPy,它使用优化的 C 实现。

    以上所有内容均适用于 CPython,即标准 Python 实现。还有其他 Python 实现(如Cython)提供自己的优化;我对这些不太熟悉。

    • 3
  2. chrslg
    2024-12-12T23:00:21+08:002024-12-12T23:00:21+08:00

    (请参阅评论以了解您的代码为什么不起作用,因为这不是真正的问题)。

    正如 Josh 所说,CPython 不进行优化。因此循环展开可能有用(假设简单地不使用 Python 中的循环,例如让其numpy为您完成,这不是解决方案)。

    但是,我想指出的是,你关于解释器“即时”执行操作并因此无法优化代码的观点是错误的。

    解释器绝不会只解释文本;即使是最古老的解释器,现代的解释器更不会。它们的工作原理有点像编译器,但有不同的阶段:标记化,然后是语法分析,将文本转换为标记树,然后是语义分析,例如,将标识符链接到其定义(这就是为什么即使在解释器中,与我们有时在这里读到的相反,从性能角度来看,变量名是 1 个字符还是 1000 个字符并不重要。名称在执行开始之前就从代码中消失了),如果语言是类型语言,则进行一些类型检查或评估,等等。

    在那个阶段,解释器可以展开循环:它可以看到循环计数器未在循环外使用(因此,不需要保留它),并决定将树转换为通过for重复指令来替换)。

    甚至编译器与解释器之间唯一不同的阶段,即代码生成阶段,如今也经常由解释器运行,因为大多数现代解释器首先为虚拟机编译,然后运行虚拟机代码。因此,在这个阶段也可以执行一些优化,例如中间结果的分解和类型。

    因此,这不会改变答案:不,CPython 并没有真正进行优化,是的,使用 CPython,展开循环有时会节省时间,这并不奇怪(当然,这取决于循环中的内容。如果在循环内部之前增加计数器并将其与范围进行比较的成本可以忽略不计,那么展开它就有点徒劳了)。但原因不是“因为 python 是一个解释器,而解释器与编译器不同,无法进行优化”。现代解释器基本上是编译器和虚拟机。甚至更传统的解释器也会解释表示程序的树,而不是文本代码,并且一些优化(如展开循环)可以在构建该树的阶段直接完成(即在.py读取文件后,即使未执行)。这只是因为 CPython 选择不这样做。

    (而 CPython 选择不这样做,因为 python 点不是最优的。如果您需要快速代码,那么您不应该用 python 编码。您应该依赖 numpy 或类似的东西,它们不是用 python 编码的。或者使用您自己的扩展,使用 Python/C Api、ctypes、numba、cython 或......)

    • 2

相关问题

  • 如何将 for 循环拆分为 3 个单独的数据框?

  • 如何检查 Pandas DataFrame 中的所有浮点列是否近似相等或接近

  • “load_dataset”如何工作,因为它没有检测示例文件?

  • 为什么 pandas.eval() 字符串比较返回 False

  • Python tkinter/ ttkboostrap dateentry 在只读状态下不起作用

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    Vue 3:创建时出错“预期标识符但发现‘导入’”[重复]

    • 1 个回答
  • Marko Smith

    为什么这个简单而小的 Java 代码在所有 Graal JVM 上的运行速度都快 30 倍,但在任何 Oracle JVM 上却不行?

    • 1 个回答
  • Marko Smith

    具有指定基础类型但没有枚举器的“枚举类”的用途是什么?

    • 1 个回答
  • Marko Smith

    如何修复未手动导入的模块的 MODULE_NOT_FOUND 错误?

    • 6 个回答
  • Marko Smith

    `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它?

    • 3 个回答
  • Marko Smith

    何时应使用 std::inplace_vector 而不是 std::vector?

    • 3 个回答
  • Marko Smith

    在 C++ 中,一个不执行任何操作的空程序需要 204KB 的堆,但在 C 中则不需要

    • 1 个回答
  • Marko Smith

    PowerBI 目前与 BigQuery 不兼容:Simba 驱动程序与 Windows 更新有关

    • 2 个回答
  • Marko Smith

    AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String”

    • 1 个回答
  • Marko Smith

    我正在尝试仅使用海龟随机和数学模块来制作吃豆人游戏

    • 1 个回答
  • Martin Hope
    Aleksandr Dubinsky 为什么 InetAddress 上的 switch 模式匹配会失败,并出现“未涵盖所有可能的输入值”? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge 为什么这个简单而小的 Java 代码在所有 Graal JVM 上的运行速度都快 30 倍,但在任何 Oracle JVM 上却不行? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini 具有指定基础类型但没有枚举器的“枚举类”的用途是什么? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer 何时应使用 std::inplace_vector 而不是 std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller 在 5.2 版中,bash 条件语句中的 [[ .. ]] 中的分号现在是可选的吗? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench 为什么双破折号 (--) 会导致此 MariaDB 子句评估为 true? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng 为什么 `dict(id=1, **{'id': 2})` 有时会引发 `KeyError: 'id'` 而不是 TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String” 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB 为什么 GCC 生成有条件执行 SIMD 实现的代码? 2024-02-17 06:17:14 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve