@@ -95,36 +95,43 @@ public class Main
95
95
### 回答
96
96
#### 什么是分支预测?
97
97
看看这个铁路分岔口
98
+ ![ ] ( http://i.stack.imgur.com/muxnt.jpg )
99
+ Image by Mecanismo, via Wikimedia Commons. Used under the CC-By-SA 3.0 license.
100
+
98
101
为了理解这个问题,想象一下,如果我们回到19世纪.
102
+
99
103
你是在分岔口的操作员。当你听到列车来了,你没办法知道这两条路哪一条是正确的。然后呢,你让列车停下来,问列车员哪条路是对的,然后你才转换铁路。
104
+
100
105
** 火车很重有很大的惯性。所以他们得花费很长的时间开车和减速。**
106
+
101
107
是不是有个更好的办法呢?你猜测哪个是火车正确的行驶方向
102
108
- 如果你猜对了,火车继续前行
103
109
- 如果你猜错了,火车得停下来,返回去,然后你在换条路。
104
110
105
111
** 如果你每次都猜对了,那么火车永远不会停下来。**
106
- ** 如果你猜错太多次,那么火车会花费很多时间来停车,返回,然后在启动 **
112
+ ** 如果你猜错太多次,那么火车会花费很多时间来停车,返回,然后再启动 **
107
113
108
114
----------------------------------------------------------
109
115
** 考虑一个if条件语句** :在处理器层面上,这是一个分支指令:
110
- http://i.stack.imgur.com/pyfwC.png
111
- 你是个处理器当你看到一个分支的时候。你没办法知道下一条指令在哪的时候,你该怎么办呢?你暂停执行一直等到前面的指令完成,然后你在继续执行正确的下一条指令。
112
- 现代的处理器很复杂并且有很长的管道。所以他需要很长的时间 "热身"和 "冷却"
116
+ ![ ] ( http://i.stack.imgur.com/pyfwC.png )
117
+ 当处理器看到这个分支时,没办法知道哪个将是下一条指令。该怎么办呢?貌似只能暂停执行,直到前面的指令完成,然后再继续执行正确的下一条指令?
118
+ 现代处理器很复杂,因此它需要很长的时间 "热身"、 "冷却"
113
119
114
120
是不是有个更好的办法呢?你猜测下一个指令在哪!
115
121
- 如果你猜对了,你继续执行。
116
- - 如果你猜错了,你需要清理管道 ,返回到那个出错的分支,然后你才能继续。
122
+ - 如果你猜错了,你需要flush the pipeline ,返回到那个出错的分支,然后你才能继续。
117
123
118
124
** 如果你每次都猜对了** ,那么你永远不会停
119
125
** 如果你猜错了太多次** ,你就要花很多时间来滚回,重启。
120
126
121
127
-------------------------------------------------------
122
- 这就是分支预测。我承认这不是一个好的类比,因为火车可以用旗帜来作为方向的标识。但是在电脑中,处理器不会知道哪一个分支直到走到最后的时候。
123
- 所以怎样能很好的预测,在尽可能的使火车必须返回的次数变小?你看看火车之前的选择历史,如果这个火车往左的概率是99%。那么你猜左,反之亦然。如果每三次走这条路,你选择也按这个规律。
128
+ 这就是分支预测。我承认这不是一个好的类比,因为火车可以用旗帜来作为方向的标识。但是在电脑中,处理器不能知道哪一个分支将走到最后。
129
+
130
+ 所以怎样能很好的预测,尽可能地使火车必须返回的次数变小?你看看火车之前的选择过程,如果这个火车往左的概率是99%。那么你猜左,反之亦然。如果每3次会有1次走这条路,那么你也按这个三分之一的规律进行。
124
131
125
132
** 换句话说,你试着定下一个模式,然后按照这个模式去执行** 。这就差不多是分支预测是怎么工作的。
126
133
127
- 大多数的应用都有很好的分支预测。所以现代的分支预测者通常能实现大于90 %的命中率。但是当面对没有模式识别的无法预测的分支 ,那分支预测基本就没用了。
134
+ 大多数的应用都有很好的分支预测。所以现代的分支预测器通常能实现大于90 %的命中率。但是当面对没有模式识别、无法预测的分支 ,那分支预测基本就没用了。
128
135
129
136
如果你想知道更多:[ Branch predictor" article on Wikipedia] ( https://en.wikipedia.org/wiki/Branch_predictor ) .
130
137
@@ -135,9 +142,9 @@ http://i.stack.imgur.com/pyfwC.png
135
142
if (data[c] >= 128)
136
143
sum += data[c];
137
144
```
138
- 注意到数据是平局分布在0到255之间的 。当数据排好序后,大概有一半的的迭代数据不会进入这个条件语句,在这之后的数据会进入该条件语句 .
145
+ 注意到数据是分布在0到255之间的 。当数据排好序后,基本上前一半大的的数据不会进入这个条件语句,而后一半的数据,会进入该条件语句 .
139
146
140
- 连续的进入同一片区域很多次 ,这对分支预测是非常友好的。甚至一个简单的饱和计数器会成功预测出分支除了一些需要转换的数据 。
147
+ 连续的进入同一个执行分支很多次 ,这对分支预测是非常友好的。可以更准确地预测,从而带来更高的执行效率 。
141
148
142
149
##### 快速理解一下
143
150
```
@@ -173,7 +180,7 @@ sum += ~t & data[c];
173
180
```
174
181
这消灭了分支,把它替换成按位操作.
175
182
176
- (说明:这个技巧不是非常严格的等同于原来的if条件语句。但是在` data[] ` 所有输入值这个情况下是用的 )
183
+ (说明:这个技巧不是非常严格的等同于原来的if条件语句。但是在` data[] ` 当前这些值下是OK的 )
177
184
178
185
** 使用的设备参数是:Core i7 920 @ 3.5 GHz**
179
186
C++ - Visual Studio 2010 - x64 Release
@@ -204,23 +211,24 @@ seconds = 3.113581453
204
211
// Branchless - Sorted
205
212
seconds = 3.186068823
206
213
```
207
- 意见 :
208
- - 用了分支:没有排序和排序的数据有很大的区别
209
- - 用了上面的技巧:对于排没排序的数据,没有很大的区别
210
- - 在使用C++的情况下,用了小技巧事实上比用排好序的分支要慢上一点点 。
214
+ 结论 :
215
+ - 用了分支(if):没有排序和排序的数据,效率有很大的区别
216
+ - 用了上面提到的按位操作替换:排序与否,效率没有很大的区别
217
+ - 在使用C++的情况下,按位操作还是要比排好序的分支操作要慢 。
211
218
212
219
一般的建议是尽量避免在关键循环上出现对数据很依赖的分支。(就像这个例子)
213
220
214
221
------------------------------------------------
215
222
216
223
更新:
217
224
218
- - GCC 4.6.1 用了 ` -O3 ` or ` -ftree-vectorize ` 在64位机器上,数据有没有排序,都是一样快。
225
+ - GCC 4.6.1 用了 ` -O3 ` or ` -ftree-vectorize ` , 在64位机器上,数据有没有排序,都是一样快。
219
226
** ...**
220
227
** ...**
221
- ** ...**
222
- 等等一下
223
- 说明了现代编译器发展的已足够成熟能够各种疯狂的优化代码
228
+ ** ...等各种例子**
229
+
230
+
231
+ 说明了现代编译器越发成熟强大,可以在这方面充分优化代码的执行效率
224
232
225
233
### 相关内容
226
234
@@ -240,4 +248,7 @@ CPU的流水线指令执行
240
248
241
249
242
250
** stackoverflow链接** :
251
+
252
+ 这个问题的所有回答中,最高的回答,获取了上万个vote,还有很多个回答,非常疯狂,大家觉得不过瘾可以移步到这里查看
253
+
243
254
http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array
0 commit comments