gpt4 book ai didi

java - 分支预测不起作用?

转载 作者:搜寻专家 更新时间:2023-10-30 20:01:06 24 4
gpt4 key购买 nike

引用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];

未排序的数组给出了大约。同样的结果,我想问一下为什么分支预测在这种情况下不起作用?

最佳答案

这个我用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 微秒的运行时间。如果我注释掉涉及

的行
data[c] = data[c] >= 128? 128 : 127;

时间没有变化。这消除了所有算术考虑并专注于分支预测。如果我使用

data[c] = 127;

我得到 13 微秒,如果我使用

data[c] = 128;

我得到 16 微秒。这是“基本情况”,强调不断分支决策之间的差异。

我的结论:这肯定是低级分支预测的效果。

JIT 能否反转循环?

现在让我们分析一下您的干预。如果我使用上面代码中提供的公式,但更改

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 分支中的赋值效果,但不涉及任何跳转,因此消除了与分支预测失败相关的任何惩罚。这很好的解释了实际效果。

关于java - 分支预测不起作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21432400/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com