【问题标题】:Why is processing a sorted array not faster than an unsorted array in Python?为什么在 Python 中处理排序数组不比未排序数组快?
【发布时间】:2012-10-02 07:17:45
【问题描述】:

在这篇 Why is processing a sorted array faster than random array 的帖子中,它说分支预测是排序数组中性能提升的原因。

但我只是尝试了使用 Python 的示例;而且我认为排序数组和随机数组之间没有区别(我尝试了 bytearray 和数组;并使用 line_profile 来分析计算)。

我错过了什么吗?

这是我的代码:

from array import array
import random
array_size = 1024
loop_cnt = 1000
# I also tried 'array', and it's almost the same
a = bytearray(array_size)
for i in xrange(array_size):
    a.append(random.randint(0, 255))
#sorted                                                                         
a = sorted(a)
@profile
def computation():
    sum = 0
    for i in xrange(loop_cnt):
        for j in xrange(size):
            if a[j] >= 128:
                sum += a[j]

computation()
print 'done'

【问题讨论】:

  • sorted(a) 返回另一个已排序的列表,但不会修改 a。甚至要让代码按照你的想法去做,你必须使用a = sorted(a),或者更好的是a.sort()
  • 您可能想在此处查看 python 的结果stackoverflow.com/a/18419405/1903116
  • stackoverflow.com/q/11227809/3145716 查错。这可能会有所帮助。
  • python 使用 timsort 可能会产生一些影响...fwiw.
  • @rogerdpack:排序算法无关紧要;所有稳定的算法都会产生相同的结果。排序时间在这里没有描述。

标签: python performance


【解决方案1】:

我可能错了,但我看到链接问题和您的示例之间存在根本区别:Python 解释字节码,C++ 编译为本机代码。

在 C++ 代码中,if 直接转换为 cmp/jl 序列,CPU 分支预测器可以将其视为特定于该循环的单个“预测点”。

在 Python 中,比较实际上是几个函数调用,因此有 (1) 更多开销和 (2) 我认为执行该比较的代码是解释器中用于所有其他整数比较的函数 - 所以这是一个“预测spot" 不特定于当前块,这使分支预测器更难正确猜测。


编辑:另外,正如this 论文中所述,解释器中有更多间接分支,因此 Python 代码中的这种优化可能会被分支错误预测所掩盖解释器本身。

【讨论】:

    【解决方案2】:

    两个原因:

    • 您的数组太小,无法显示效果。
    • Python 的开销比 C 高,因此整体效果不太明显。

    【讨论】:

    • 这个程序在我的 mac-air 上需要 1.5 秒,更大的阵列消耗太多时间;我只是不想等待。
    • “我只是不想等待”所以你更喜欢我们为你做...?
    • @dda 对不起,我的意思是,当配置如上时,该功能已经需要 1.5 秒;如果我们可以从排序数组中获得一些性能提升,我们肯定可以看到它。实际上,我将数组大小更改了 10 倍,或者将循环计数更改了 10 倍,执行时间线性增加。
    • 我对我的 MBP 进行了测试,将 array_sizeloop_cnt 乘以 10,结果如下:随机数组:9.97857904434 排序数组:7.98291707039
    【解决方案3】:

    我将原始代码移植到 Python 并使用 PyPy 运行它。我可以确认排序数组的处理速度比未排序数组快,并且无分支方法也可以消除运行时间类似于排序数组的分支。我相信这是因为 PyPy 是一个 JIT 编译器,所以分支预测正在发生。

    [编辑]

    这是我使用的代码:

    随机导入 进口时间 def runme(数据): 总和 = 0 开始 = time.time() 对于 xrange(100000) 中的 i: 对于数据中的 c: 如果 c >= 128: 总和 += c 结束 = time.time() 打印结束 - 开始 打印总和 def runme_branchless(数据): 总和 = 0 开始 = time.time() 对于 xrange(100000) 中的 i: 对于数据中的 c: t = (c - 128) >> 31 总和 += ~t & c 结束 = time.time() 打印结束 - 开始 打印总和 数据 = 列表() 对于 xrange(32768) 中的 i: data.append(random.randint(0, 256)) sorted_data = 排序(数据) 运行(排序数据) 运行(数据) runme_branchless(sorted_data) runme_branchless(数据)

    【讨论】:

    • 在配备 2.53 GHz Intel Core 2 Duo 和 PyPy 1.9.0 的 MBP 中,结果为:// Branch - Random seconds = 36.2439880371 // Branch - Sorted seconds = 18.3833880424 // Branchless - Random seconds = 13.1689388752 // Branchless - Sorted seconds = 12.3706789017
    【解决方案4】:

    sorted() 返回排序后的数组,而不是就地排序。你实际上是在测量同一个数组两次。

    【讨论】:

    • 我只是把它改成了“a = sorted(a)”;还是一样
    【解决方案5】:

    点击here查看更多答案和类似问题。数据排序后性能大幅提升的原因是分支预测惩罚被移除,正如 Mysticial 的回答中所解释的那样。

    【讨论】:

      猜你喜欢
      • 2012-06-28
      • 2012-12-11
      相关资源
      最近更新 更多