【问题标题】:Python loop slower than Excel VBA?Python循环比Excel VBA慢?
【发布时间】:2021-04-15 06:35:45
【问题描述】:

我在 excel (VBA) 和 python 之间运行了一个小测试,执行一个简单的循环。下面列出的代码。令我惊讶的是,vba 比 python 快得多。快了将近 6 倍。我认为由于 python 通过命令行运行,性能会更好。你们有这方面的cmet吗?

Python

import time
import ctypes  # An included library with Python install.
start_time = time.time()

for x in range(0, 1000000):
    print x

x = ("--- %s seconds ---" % (time.time() - start_time))
ctypes.windll.user32.MessageBoxA(0, x, "Your title", 1)

Excel (VBA)

Sub looptest()

Dim MyTimer As Double

MyTimer = Timer

Dim rng As Range, cell As Range
Set rng = Range("A1:A1000000")

x = 1
For Each cell In rng

    cell.Value = x
    x = x + 1
Next cell


MsgBox Timer - MyTimer
End Sub

【问题讨论】:

    标签: python vba excel


    【解决方案1】:

    您的两个代码示例没有做同样的事情。在 Python 代码中,内部循环必须:

    • range(0, 1000000) 中询问下一个号码。
    • 显示它。

    在 VBA 代码中,Excel 必须:

    • 请求Range("A1:A1000000") 中的下一个单元格(与 Python 范围无关)。
    • 设置cell.Value 属性。
    • 运行 Excel 在更改单元格时执行的各种代码。
    • 检查是否需要重新计算任何公式。
    • 显示它。
    • 递增x

    让我们重写它,以便 Python 和 VBA 循环做同样的事情,尽可能接近:

    Python

    import time
    import ctypes
    start_time = time.time()
    
    x = 0
    while x <= 1000000:
        x = x + 1
    
    x = ("--- %s seconds ---" % (time.time() - start_time))
    ctypes.windll.user32.MessageBoxA(0, x, "Your title", 1)
    

    VBA

    Declare Function QueryPerformanceCounter Lib "kernel32" (t As Currency) As Boolean
    Declare Function QueryPerformanceFrequency Lib "kernel32" (t As Currency) As Boolean
    
    Sub looptest()
        Dim StartTime As Currency
        QueryPerformanceCounter StartTime
    
        x = 0
        Do While x <= 1000000
            x = x + 1
        Loop
    
        Dim EndTime As Currency
        QueryPerformanceCounter EndTime
        Dim Frequency As Currency
        QueryPerformanceFrequency Frequency
    
        MsgBox Format$((EndTime - StartTime) / Frequency, "0.000")
    End Sub
    

    在我的计算机上,Python 大约需要 96 毫秒,而 VBA 需要 33 毫秒——VBA 的执行速度要快三倍。如果你输入 Dim x As Long,它的执行速度会快 6 倍。

    为什么?好吧,让我们看看每个是如何运行的。 Python 在内部将您的 .py 文件编译为 .pyc,并在 Python VM 下运行它。 Another answer describes the Python case in detail. Excel 将 VBA 编译成 MS P-Code,并在 Visual Basic VM 下运行。

    此时,python.exe 是命令行而 Excel 是 GUI 并不重要。 VM 运行您的代码,它在您计算机的内部更深处。性能取决于编译代码中有哪些特定指令,以及 VM 运行这些指令的效率如何。在这种情况下,VB VM 运行其 P-Code 比 Python VM 运行其.pyc 更快。

    【讨论】:

    • 感谢您的资源。很有见地。我想如果我们想提高性能,我们可以使用 Cpython 之类的东西来将代码进一步分解为编译语言。看看我们是否可以通过这种方法击败 VBA 的性能会很有趣。
    • @Jimmyn 你是说 Cython 吗?您可能已经在使用 CPython,这是该语言的参考实现。
    • 那些时间太小了:在基于操作系统的平台上,96 对 33 毫秒的运行时间仍然太小,因为它与上下文切换、页面交换、JIT 编译、惰性的数量级相同eval 等。我会将主循环包装在一个外循环中并运行至少 10-15 秒。最后,两者都应该做相同的 ADD/JMP + 分支错误预测,所以我希望它们是相同的。不像他们的编译器/解释器有很多不同的地方。
    【解决方案2】:

    这方面的缓慢部分是print。打印到控制台非常慢,所以你应该完全避免它。我认为在 Excel 中设置单元格值会更快。

    如果你想比较计算速度,你不应该在循环中有任何 I/O。相反,只计算处理整个循环所花费的时间,而不在内部做任何事情(或者做一些简单的事情,比如添加一个数字或其他事情)。如果你这样做,你会发现 Python 非常快。

    【讨论】:

    • 我调整了代码以在没有 print 语句的情况下运行,你就在现场。谢谢,我对 python 的信心恢复了。
    • 我很高兴听到这个消息 ;)
    【解决方案3】:

    它会随着您需要执行的计算而变化,并且很难通过简单的比较来以简单的方式给出更快的证明。所以我会以一种通用的方式分享我的经验,没有证据,但在可能会产生很大差异的事情上与一些 cmet 分享。

    根据我的经验,如果您将一个简单的 for 循环与纯 Python 和纯 VBA 之间完全相同的代码进行比较,VBA 的速度大约快 3 倍。但是没有人用不同的语言做同样的事情。您必须应用每种语言的最佳实践。

    如果您应用 VBA 最佳实践,您可以通过声明变量和其他在 python 中不可用的类似优化来使其更快。根据我的经验,这可以使代码快 2-3 倍。因此,您可以使 VBA 代码比 python 中的简单 for 循环快 6-9 倍。

    另一方面,如果您应用 python 最佳实践,您通常不会编写常规的 for 循环。您将使用列表推导,或者您将使用在编译的 C 库中运行的 numpy 和 scipy。这些解决方案比 VBA 代码快得多。

    一般来说,如果你执行复杂的矩阵计算,你可以用 numpy 和 scipy 来做,python 会比 VBA 快。在其他情况下,VBA 更快。

    在 python 中,您也可以使用 Numba,这会增加代码的一些复杂性,但它有助于生成编译后的代码并处理在 GPU 中运行它。它将使您的代码更快。

    这是我对主要涉及内部数组的纯计算的经验。我没有将 I/O 与 GUI、外部文件和数据库、网络通信或 API 的性能进行比较。

    【讨论】:

      猜你喜欢
      • 2017-05-12
      • 1970-01-01
      • 2017-05-26
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多