最近在看 Python epoll howto 的时候,发现里面对比一个 event 是不是某种 EVENT 类型用的是 &
操作符,比如 elif event & select.EPOLLIN
。一开始还以为是写错了,后来想了一下这样确实能起到 ==
的作用,只要事件的实际数字值特殊设计的话。
只比较相等的话,只要不存在包含关系就可以。比如最简单的,每一个 bit 的1表示一个类型(这里都以一个 bytes 作例子吧。
1 2 3 4 |
00000001 00000010 00000100 00001000 |
上面4个数字分别是1,2,4,8,分别可以表示四种类型。对比的时候,除非两个类型相等,否则都是 0(Flase)。或者可以用下面这种方式,使用两个1,可以表示的类型多一些。
1 2 3 4 5 6 |
00001100 00001010 00001001 00000110 00000101 00000011 |
上面这种也可以达到类似的效果。如果存在包含关系的话就不行了,在不相等的情况下依然可能得到非0值。但是如果不是比较相等的情况,而是比较是不是包含,就可以使用这种表示了。比如 Unix 的文件权限系统,用 4 2 1 来表示执行:
1 2 3 |
00000100 -------读 00000010 -------写 00000001 -------执行 |
如果验证 6 是否有读权限的话也可以用 &
:
1 2 |
00000110 -------6的表示 00000100 -------4写权限的表示 |
结果是非0值,表示拥有此权限。
为什么这么写呢,估计是C程序的风格吧,我用 Python 测试了一下性能,&
操作是比 ==
效率高的。因为 epoll 这部分的代码是非常频繁的操作,能节省一些性能的话,还是很可观的。
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 效率高一些吧。
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 |
记得 CSAPP 经常提到位运算优化。
其实有好几本书就是专门讲 binary operator 的。