最近在看 Python epoll howto 的时候,发现里面对比一个 event 是不是某种 EVENT 类型用的是 & 操作符,比如 elif event & select.EPOLLIN 。一开始还以为是写错了,后来想了一下这样确实能起到 == 的作用,只要事件的实际数字值特殊设计的话。

只比较相等的话,只要不存在包含关系就可以。比如最简单的,每一个 bit 的1表示一个类型(这里都以一个 bytes 作例子吧。

Python

1

2

3

4

00000001

00000010

00000100

00001000

上面4个数字分别是1,2,4,8,分别可以表示四种类型。对比的时候,除非两个类型相等,否则都是 0(Flase)。或者可以用下面这种方式,使用两个1,可以表示的类型多一些。

Python

1

2

3

4

5

6

00001100

00001010

00001001

00000110

00000101

00000011

上面这种也可以达到类似的效果。如果存在包含关系的话就不行了,在不相等的情况下依然可能得到非0值。但是如果不是比较相等的情况,而是比较是不是包含,就可以使用这种表示了。比如 Unix 的文件权限系统,用 4 2 1 来表示执行:

Python

1

2

3

00000100    -------

00000010    -------

00000001    -------执行

如果验证 6 是否有读权限的话也可以用 & :

Python

1

2

00000110  -------6的表示

00000100  -------4写权限的表示

结果是非0值,表示拥有此权限。

为什么这么写呢,估计是C程序的风格吧,我用 Python 测试了一下性能,& 操作是比 == 效率高的。因为 epoll 这部分的代码是非常频繁的操作,能节省一些性能的话,还是很可观的。

Python

1

2

3

4

5

6

[py37] tmp python -m timeit '1 == 4; 4 == 8; 8 == 16; 1 == 16'

5000000 loops, best of 5: 95.6 nsec per loop

[py37] tmp python -m timeit '1 & 4; 4& 8; 8& 16; 1& 16'

50000000 loops, best of 5: 8.17 nsec per loop

[py37] tmp python -m timeit '1 is 4; 4 is 8; 8 is 16; 1 is 16'

5000000 loops, best of 5: 52.4 nsec per loop

通过反编译发现,其实 & 编译成 Python 字节码是 BINARY_AND ,而 == 是 COMPARE_OP。应该是 BINARY_AND 效率会比 COMPARE_OP 效率高一些吧。

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

[py37] tmp ipython

Python 3.7.0 (default, Jul  7 2018, 19:33:03)

Type 'copyright', 'credits' or 'license' for more information

IPython 6.4.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from dis import dis

In [2]: a, b = 1, 4

In [3]: def compare1():

   ...:     a & b

   ...:     a == b

   ...:

In [4]: dis (compare1)

  2           0 LOAD_GLOBAL              0 (a)

              2 LOAD_GLOBAL              1 (b)

              4 BINARY_AND

              6 POP_TOP

  3           8 LOAD_GLOBAL              0 (a)

             10 LOAD_GLOBAL              1 (b)

             12 COMPARE_OP               2 (==)

             14 POP_TOP

             16 LOAD_CONST               0 (None)

             18 RETURN_VALUE