为什么按位运算符比乘法/除法/模数慢?

iz_*_*iz_ 9 python optimization bitwise-operators micro-optimization

众所周知,乘法,整数除法和2的幂的模可以作为按位运算更有效地重写:

>>> x = randint(50000, 100000)
>>> x << 2 == x * 4
True
>>> x >> 2 == x // 4
True
>>> x & 3 == x % 4
True
Run Code Online (Sandbox Code Playgroud)

在诸如C/C++和Java等编译语言中,测试表明按位运算通常比算术运算更快.(见这里这里).但是,当我在Python中测试这些时,我得到了相反的结果:

In [1]: from random import randint
   ...: nums = [randint(0, 1000000) for _ in range(100000)]

In [2]: %timeit [i * 8 for i in nums]
7.73 ms ± 397 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [3]: %timeit [i << 3 for i in nums]
8.22 ms ± 368 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [4]: %timeit [i // 8 for i in nums]
7.05 ms ± 393 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [5]: %timeit [i >> 3 for i in nums]
7.55 ms ± 367 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [6]: %timeit [i % 8 for i in nums]
5.96 ms ± 503 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [7]: %timeit [i & 7 for i in nums]
8.29 ms ± 816 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,按位运算比它们的算术运算慢,特别是对于模数.我为另一组数字重复了这个测试,得到了相同的结果.是否有一个原因?如果这很重要,这些测试都在CPython 3.6.7中.

use*_*ica 11

*,%/所有人都有单一"肢体"整数的快速路径.<<,>>&没有.他们正在经历通用的任意精度代码路径.

  • @ Tomothy32:"肢体"是一个基数-2 ^ 30"数字",存储为"uint32_t"或等效的二进制整数.CPython将大整数存储在该大小的块中.就像你可以一次在纸上添加十进制数字一样,使用base-2 ^ 30数字可以让CPython在CPU执行的每个原语添加操作中完成大量工作.(使用base 2 ^ 32块将允许在asm中使用add/add-with-carry来利用大多数架构上的大整数的硬件支持,但这不是CPython所做的.2 ^ 30虽然有一些优点,但是,esp .对于没有asm的纯C.) (4认同)