9 python performance built-in branchless
我发现了 2 个无分支函数,它们在 python 中查找两个数字的最大值,并将它们与 if 语句和内置 max 函数进行比较。我认为无分支或内置函数将是最快的,但最快的是 if 语句函数。有人知道这是为什么吗?以下是功能:
If 语句(25000 次操作需要 2.16 秒):
def max1(a, b):
if a > b:
return a
return b
Run Code Online (Sandbox Code Playgroud)
内置(25000次操作4.69秒):
def max2(a, b):
return max(a, b)
Run Code Online (Sandbox Code Playgroud)
Branchless 1(25000 次操作需要 4.12 秒):
def max3(a, b):
return (a > b) * a + (a <= b) * b
Run Code Online (Sandbox Code Playgroud)
Branchless 2(25000 次操作需要 5.34 秒):
def max4(a, b):
diff = a - b
return a - (diff & diff >> 31)
Run Code Online (Sandbox Code Playgroud)
kay*_*ya3 14
您对分支与无分支代码的期望适用于汇编和 C 等低级语言。无分支代码在低级语言中可以更快,因为它可以防止因分支预测缺失而导致的速度减慢。(注意:这意味着无分支代码可以更快,但不一定如此。)
Python 是一种高级语言。假设您正在使用 CPython 解释器:对于您执行的每条字节码指令,解释器必须根据操作码类型进行分支,通常还需要根据许多其他内容进行分支。例如,即使是简单的<
运算符也需要一个分支来检查<
操作码,另一个分支来检查对象的类是否实现了__lt__
方法,更多的分支来检查右侧值是否是有效类型,以便进行比较执行,可能还有其他几个分支。由于这些原因,即使是所谓的“无分支”代码实际上也会导致大量分支。
因为 Python 是如此高级,所以与单个机器代码指令相比,每个字节码指令实际上做了相当多的工作。因此,像这样的简单代码的性能主要取决于必须解释多少字节码指令:
max1
函数必须执行三个局部变量加载、比较、条件跳转和返回。那是六个。max2
函数执行两次局部变量加载,一次加载全局变量(引用内置max
),并且还进行函数调用;需要传递参数,并且与其他字节码指令相比相对昂贵。最重要的是,内置函数本身必须完成与您自己的max1
函数相同的工作,所以难怪max2
。max3
函数执行六次局部变量加载、两次比较、两次乘法、一次加法和一次返回。这是 12 条指令,因此我们预计它需要大约两倍的时间max1
。max4
加载五次局部变量,一次存储到局部变量,一次加载常量,两次减法,一次位移,一次按位“与”,一次返回。这又是十二条指令。也就是说,请注意,如果我们直接将您的函数max1
与内置函数进行比较max
,而不是您的函数max2
有额外的函数调用,那么您的max1
函数仍然比内置函数快一点max
。这可能是因为内置函数max
接受可变数量的参数,这可能涉及构建参数元组,并且内置函数max
还有一个分支来检查是否使用单个可迭代参数(例如max([3, 1, 4, 2])
)调用它,并且以不同的方式处理这种情况;你的max1
函数不做这些事情。