在æ到this问题时,ç”案指出未排åºçš„æ•°ç»„èŠ±è´¹æ›´å¤šæ—¶é—´ï¼Œå› ä¸ºå®ƒæœªé€šè¿‡åˆ†æ”¯é¢„æµ‹æµ‹è¯•ã€‚ä½†æ˜¯å¦‚æžœæˆ‘ä»¬å¯¹ç¨‹åºè¿›è¡Œå¾®å°çš„改动:
import java.util.Arrays;
import java.util.Random;
public class Main{
public static void main(String[] args) {
// Generate data
int arraySize = 32768;
int data[] = new int[arraySize];
Random rnd = new Random(0);
for (int c = 0; c < arraySize; ++c) {
data[c] = rnd.nextInt() % 256;
}
// !!! With this, the next loop runs faster
Arrays.sort(data);
// Test
long start = System.nanoTime();
long sum = 0;
for (int i = 0; i < 100000; ++i) {
// Primary loop
for (int c = 0; c < arraySize; ++c) {
if (data[c] >= 128) {
sum = data[c];
}
}
}
System.out.println((System.nanoTime() - start) / 1000000000.0);
System.out.println("sum = " + sum);
}
}
这里我已ç»æ›¿æ¢äº†ï¼ˆæ¥è‡ªåŽŸå§‹é—®é¢˜ï¼‰
if (data[c] >= 128)
sum += data[c];
与
if (data[c] >= 128)
sum = data[c];
未排åºçš„数组给出约。åŒæ ·çš„结果,我想问为什么分支预测在这ç§æƒ…况下ä¸èµ·ä½œç”¨ï¼Ÿ
ç”案 0 :(得分:10)
我用jmhæ¥åˆ†æžè¿™ä¸ªã€‚这是我的代ç :
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 2, time = 1)
@Measurement(iterations = 3, time = 1)
@State(Scope.Thread)
@Fork(2)
public class Comparison
{
static final int SIZE = 1<<15;
final int[] data = new int[SIZE];
@Setup
public void setup() {
int i = 1;
for (int c = 0; c < SIZE; ++c) data[c] = (i*=611953);
for (int c = 0; c < SIZE; ++c) data[c] = data[c] >= 128? 128 : 127;
}
@GenerateMicroBenchmark
public long sum() {
long sum = 0;
for (int c = 0; c < SIZE; ++c) if (data[c] >= 128) sum += data[c];
return sum;
}
}
注æ„我ä¸ä½¿ç”¨æŽ’åºæˆ–éšæœºæ•°ç”Ÿæˆ;它们是ä¸å¿…è¦çš„并å‘症。使用上é¢ä»£ç ä¸ä½¿ç”¨çš„å…¬å¼ï¼š
data[c] = (i*=611953);
我的è¿è¡Œæ—¶é—´ä¸º132μs。如果我注释掉涉åŠ
çš„è¡Œdata[c] = data[c] >= 128? 128 : 127;
æ—¶é—´æ ¹æœ¬æ²¡æœ‰å˜åŒ–。这消除了所有算术考虑,并侧é‡äºŽåˆ†æ”¯é¢„测。如果我使用
data[c] = 127;
我得到13μs,如果我使用
data[c] = 128;
我得到16μs。这是“基本案例â€ï¼Œå¼ºè°ƒäº†ä¸æ–分支决ç–之间的区别。
我的结论:这ç»å¯¹æ˜¯ä½Žçº§åˆ†æ”¯é¢„测的结果。
让我们现在分æžä½ 的干预。如果我使用上é¢ä»£ç ä¸æ供的公å¼ï¼Œä½†æ›´æ”¹
if (data[c] >= 128) sum += data[c];
到
if (data[c] >= 128) sum = data[c];
然åŽæ—¶é—´ç¡®å®žä»Ž132μs下é™åˆ°27μs。
这是我对解释掉è½çš„猜测:JIT编译器å¯ä»¥åšçš„优化技巧是å转循环的方å‘。现在您的代ç å˜ä¸º
for (int c = SIZE-1; c <= 0; --c) if (data[c] >= 128) { sum = data[c]; break; }
循环已ç»çŸè·¯åˆ°è¾¾åˆ°ä¸ŽåŽŸå§‹å¾ªçŽ¯ç›¸åŒç»“果所需的最å°è¿ä»£æ¬¡æ•°ã€‚
æˆ‘æ·»åŠ äº†è¿™ä¸ª
data[SIZE-1] = 128;
到setup()
方法的末尾,但它没有改å˜æ—¶é—´ã€‚这似乎使“循环逆转â€çŒœæƒ³çš„å¤©çœŸç‰ˆæœ¬æ— æ•ˆã€‚
cmovl
在分æžè£…é…时,我å‘现:
cmp edx, 0x80
cmovl eax, ebx
cmovl
是一个æ¡ä»¶ç§»åŠ¨æŒ‡ä»¤ï¼Œå®ƒå°†æ‰§è¡Œthen
分支ä¸å‘生的赋值效果,但ä¸æ¶‰åŠä»»ä½•è·³è½¬ï¼Œå› æ¤æ¶ˆé™¤äº†ä¸Žä¹‹ç›¸å…³çš„任何惩罚分支预测失败。这是对实际效果的一个很好的解释。