一. 证明性能上:& >> %
在常识中,我们普遍认为 位运算操作符"&" 的性能比取余运算符"%"高,因为"&"是自己面向cpu的指令,而"%"可能需要转换。
int len = 10_0000_0000;
long l ;
l = System.currentTimeMillis();
for(int i=1;i<len;i++){
int c = i & i;
}
System.out.println("&: " + System.currentTimeMillis() - l);
l = System.currentTimeMillis();
for(int i=1;i<len;i++){
int c = i % i;
}
System.out.println("%: " + System.currentTimeMillis() - l);
我们多次打印后得到结果:
&: 458
%: 3438
我们大概能得到一个结论:
位运算操作符"&"的性能大概是运算符"%"的8倍到9倍间
二. netty EventExecutorChooserFactory
这是一个EventExecutor的选择器的factory,而netty的作者居然考虑到选择的时候 是用"%"还是 "&" 来实现 next功能
- 生成EventExecutorChooser
public EventExecutorChooser newChooser(EventExecutor[] executors) {
if (isPowerOfTwo(executors.length)) {
return new PowerOfTwoEventExecutorChooser(executors);
} else {
return new GenericEventExecutorChooser(executors);
}
}
- 通过是否为 2的次方。
2的次方首先要满足一个条件,在int表示的4个字节的32个位中,除了首位,有且仅有一个bit上为1,其他为0。例如:00001000
private static boolean isPowerOfTwo(int val) {
return (val & -val) == val;//看清楚,是"-",不是"~"
}
我们可以这么理解:
我们观察这种数字特征:00001000
00001000 & 11111000
刚好可以得到原始值 00001000
- 为甚我需要一个2的次方?
因为2的次方 -1 后,刚好后面的数全是1。
0001000 - 1 = 00000111
而这个数我们可以&来做循环。
基数
: 00000011
1: 00000001
2: 00000010
3: 00000011
1: 00000101
2: 00000110
...
- 最后看看源码
可以看出 如果是这种数 00001111
,尾巴连续1的数,就采用"&"去做循环next,如果不是就直接用"%"
private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;
@Override
public EventExecutor next() {
return executors[idx.getAndIncrement() & executors.length - 1];
}
}
private static final class GenericEventExecutorChooser implements EventExecutorChooser {
private final AtomicInteger idx = new AtomicInteger();
private final EventExecutor[] executors;
@Override
public EventExecutor next() {
return executors[Math.abs(idx.getAndIncrement() % executors.length)];
}
}