nop*_*ole 6 python performance bit-manipulation python-3.x
我认为否定一个字或异或一个字1应该占用处理器的 1 个时钟周期,但如果使用 Python3,以下代码计算一个字的奇偶校验:
def parity(x: int) -> int:
p = 0
while x:
p = ~p
x = x & (x-1)
return p & 1
Run Code Online (Sandbox Code Playgroud)
针对 10,000 个测试用例4?s运行将始终如一地报告平均运行时间,但如果是:
def parity(x: int) -> int:
p = 0
while x:
p = p ^ 1
x = x & (x-1)
return p
Run Code Online (Sandbox Code Playgroud)
那么它是一贯的5?s。它实际上最后少了一个操作,不需要& 1。
甚至
def parity(x: int) -> int:
p = 0
while x:
p += 1
x = x & (x-1)
return p % 2
Run Code Online (Sandbox Code Playgroud)
一直在运行4?s。为了测试增量与加法,我将 更改p += 1为p += 7并且它再次始终为4?s。使用 Python3 进行否定、递增或加法比 XOR 更快的原因是什么?(如果它有任何区别,它在Mac上)。
事实证明,在 Python 中,并非所有运算符都是平等创建的。
您在问题中提到了“时钟周期”。诚然,典型的 CPU 可以在一个周期内完成许多此类任务,但是,Python 代码(在 CPython 中)的执行是由编译后的字节码决定的。这是一个操作(操作码)列表,它们映射到 CPython 运行时中的 C 代码。
C 代码这些部分的执行时间显然可能不同。事实上,您会发现它们都不与“1 个周期”操作相关联,而是与多个函数调用和分支语句相关联。
如果您反汇编您给出的两个函数,dis.dis(parity)您将看到它们的操作码在相关部分有何不同。
从第一个函数(求反)开始
10 LOAD_FAST 1 (p)
12 UNARY_INVERT
14 STORE_FAST 1 (p)
Run Code Online (Sandbox Code Playgroud)
从第二个函数(XOR):
10 LOAD_FAST 1 (p)
12 LOAD_CONST 2 (1)
14 BINARY_XOR
16 STORE_FAST 1 (p)
Run Code Online (Sandbox Code Playgroud)
最值得注意的是,UNARY_INVERT更改为BINARY_XOR. 检查Python/ceval.c中的master,switch (opcode) { ... }看看这些操作码和它们对应的代码有何不同。
这是一个小测试程序,用于比较 Python 中一些不同运算符的时间。
10 LOAD_FAST 1 (p)
12 UNARY_INVERT
14 STORE_FAST 1 (p)
Run Code Online (Sandbox Code Playgroud)
这是我在 CPython 3.7.2 64 位 (Windows) 中的结果
binary operators
+ 0.038782999999999956
- 0.03799190000000002
^ 0.05287609999999998
& 0.03716779999999997
| 0.0518267
unary operators
- 0.020763399999999987
~ 0.020213900000000007
not 0.01701140000000001
Run Code Online (Sandbox Code Playgroud)
从这些结果来看,它似乎^或BINARY_XOR实际上是最慢的操作员(在测试的操作员中)
| 归档时间: |
|
| 查看次数: |
173 次 |
| 最近记录: |