为什么添加局部变量会导致方法延迟



我最近开始阅读关于基准测试的文章,并为Android(用Java)编写它们。我知道诸如预热、垃圾收集器和编译器优化之类的问题,但不知道我面临的问题是否是由这些问题引起的。

在我的基准测试应用程序中,我创建了一个由10000个浮点变量组成的数组,并用随机值对其进行初始化。运行基准代码时:

private void runMinorBenchmarkFloat (float[] array) {
        float sum = 0;
        long startTime;
        long endTime; 
        /* Fast warm-up */
        startTime = System.nanoTime();
        for(int i=0; i<SMALL_LOOP_ITERATION_COUNT; i++)
            for(int j=0; j<TAB_SIZE; j++)
                sum += array[j];
        endTime = System.nanoTime() - startTime;
        postMessage("Warm-up for FLOAT finished in: " + endTime/1000000 + "ms.n");
        /* Main benchmark loop */
        startTime = System.nanoTime();
        for(int i=0; i<BIG_LOOP_ITERATION_COUNT; i++)
        {
            sum = 0;
            for(int j=0; j<TAB_SIZE; j++)
                sum += array[j];
        }
        endTime = System.nanoTime() - startTime;
        postMessage("Benchmark for FLOAT finished in: " + endTime/1000000 + "ms.n");
        postMessage("Final value: " + sum + "nn");
    }

在我的手机上,我有大约2秒的热身时间和20秒的"真实"循环时间。

现在,当我再添加两个浮点变量(sum2和sum3——从未在方法中使用过)时:

private void runMinorBenchmarkFloat (float[] array) {
        float sum = 0, sum2 = 0, sum3 = 0; // <------- the only code change here!!!
        long startTime;
        long endTime; 
        /* Fast warm-up */
        startTime = System.nanoTime();
        for(int i=0; i<SMALL_LOOP_ITERATION_COUNT; i++)
            for(int j=0; j<TAB_SIZE; j++)
                sum += array[j];
        endTime = System.nanoTime() - startTime;
        postMessage("Warm-up for FLOAT finished in: " + endTime/1000000 + "ms.n");
        /* Main benchmark loop */
        startTime = System.nanoTime();
        for(int i=0; i<BIG_LOOP_ITERATION_COUNT; i++)
        {
            sum = 0;
            for(int j=0; j<TAB_SIZE; j++)
                sum += array[j];
        }
        endTime = System.nanoTime() - startTime;
        postMessage("Benchmark for FLOAT finished in: " + endTime/1000000 + "ms.n");
        postMessage("Final value: " + sum + "nn");
    }

执行时间从预热的2秒跳到5秒,从实际循环的20秒跳到50秒。

常数:

SMALL_LOOP_ITERATION_COUNT = 100,000 
BIG_LOOP_ITERATION_COUNT = 1,000,000

你认为这种差异可能是由对齐问题(只是松散的想法)引起的吗?

提前感谢您的回答。

编辑:

似乎并不是每个设备上都会出现此错误。我可以在三星Galaxy S5上复制它。该项目的主要目标是制定一个小的基准。我做了四个几乎相同的函数(runMinorBenchmark____,其中_是:int、short、float、double),它们只在变量"sum"类型上有所不同。在主基准测试函数中,我调用了这些函数。由于出现了上述错误,我决定将这些函数合并为一个大函数。现在当运行测试时,我有这样的时间:1.37640ms。(用于int)2.46728ms。(简称)3.60589ms。(用于浮动)4.34467ms。(双)

我知道短是指较慢,因为类型铸造。我还认为,在将其铸造为双倍的情况下,浮动应该更慢(也许FPU每次都会将铸造类型铸造为双倍(?))。但是,当我将sumFloat的变量类型从float更改为double时,float的时间与double时间相同。我还在另一台设备上做了这个"基准测试",该设备似乎没有出现这种奇怪的行为,每次测试的时间几乎相同:约45000ms。(确实没有明显的差异)。

Dalvik VM错误(?)

我不相信这是造成你麻烦的原因。编译器肯定只是把那些未使用的变量扔掉了吗?您确定输入数组、常量或TAB_SIZE不会更改吗?

如果你仍然确定,可以运行这样的程序并将输出粘贴到这里来证明这一点:

public void proveIt() {
    float[] inputArray = new float[10000];
    for (int i = 0; i < 10000; i++) {
        inputArray[i] = 1;
    }
    postMessage("Without declaration:");
    runMinorBenchmarkFloatA(inputArray);
    postMessage("With declaration:");
    runMinorBenchmarkFloatB(inputArray);
    postMessage("And again just to make sure...");
    postMessage("Without declaration:");
    runMinorBenchmarkFloatA(inputArray);
    postMessage("With declaration:");
    runMinorBenchmarkFloatB(inputArray);
}
long TAB_SIZE = 10000;
long SMALL_LOOP_ITERATION_COUNT = 100000;
long BIG_LOOP_ITERATION_COUNT = 1000000;
private void runMinorBenchmarkFloatA(float[] array) {
    float sum = 0;
    long startTime;
    long endTime;
    /* Fast warm-up */
    startTime = System.nanoTime();
    for (int i = 0; i < SMALL_LOOP_ITERATION_COUNT; i++)
        for (int j = 0; j < TAB_SIZE; j++)
            sum += array[j];
    endTime = System.nanoTime() - startTime;
    postMessage("Warm-up for FLOAT finished in: " + endTime
            / 1000000 + "ms.n");
    /* Main benchmark loop */
    startTime = System.nanoTime();
    for (int i = 0; i < BIG_LOOP_ITERATION_COUNT; i++) {
        sum = 0;
        for (int j = 0; j < TAB_SIZE; j++)
            sum += array[j];
    }
    endTime = System.nanoTime() - startTime;
    postMessage("Benchmark for FLOAT finished in: " + endTime
            / 1000000 + "ms.n");
    postMessage("Final value: " + sum + "nn");
}
private void runMinorBenchmarkFloatB(float[] array) {
    float sum = 0, sum2 = 0, sum3 = 0;
    long startTime;
    long endTime;
    /* Fast warm-up */
    startTime = System.nanoTime();
    for (int i = 0; i < SMALL_LOOP_ITERATION_COUNT; i++)
        for (int j = 0; j < TAB_SIZE; j++)
            sum += array[j];
    endTime = System.nanoTime() - startTime;
    postMessage("Warm-up for FLOAT finished in: " + endTime
            / 1000000 + "ms.n");
    /* Main benchmark loop */
    startTime = System.nanoTime();
    for (int i = 0; i < BIG_LOOP_ITERATION_COUNT; i++) {
        sum = 0;
        for (int j = 0; j < TAB_SIZE; j++)
            sum += array[j];
    }
    endTime = System.nanoTime() - startTime;
    postMessage("Benchmark for FLOAT finished in: " + endTime
            / 1000000 + "ms.n");
    postMessage("Final value: " + sum + "nn");
}

现在,当我再添加两个浮点变量(sum2和sum3——从未在方法中使用过)时:

float sum = 0, sum2 = 0, sum3 = 0; // <------- the only code change here!!!

但是sum2sum3在该方法中被使用。它们被初始化为零。这需要时间。

如果它们没有初始化,那么生成的字节码在有和没有它们的情况下都是相同的,除了分配的堆栈帧的大小之外,这不会影响时序。

最新更新