为什么减少循环次数不会加快程序的速度?

Kra*_*ple 5 java performance matrix-multiplication

我有一个程序,可以进行大量的矩阵乘法.我以为我会通过减少代码中的循环次数来加快速度,看看会有多快(我稍后会尝试矩阵数学库).事实证明它根本不快.我已经能够用一些示例代码复制问题.我的猜测是testOne()会比testTwo()快,因为它不会创建任何新数组,因为它有第三个循环.在我的机器上,它需要两倍的运行时间:

testOne的持续时间为5000个纪元:657,loopCount:64000000

testTwo的持续时间为5000个纪元:365,loopCount:192000000

我的猜测是multOne()比较慢,multTwo()因为在multOne()CPU中没有写入顺序存储器地址multTwo().听起来不错吗?任何解释将不胜感激.

import java.util.Random;

public class ArrayTest {

    double[] arrayOne;
    double[] arrayTwo;
    double[] arrayThree;

    double[][] matrix;

    double[] input;
    int loopCount;

    int rows;
    int columns;

    public ArrayTest(int rows, int columns) {
        this.rows = rows;
        this.columns = columns;
        this.loopCount = 0;
        arrayOne = new double[rows];
        arrayTwo = new double[rows];
        arrayThree = new double[rows];
        matrix = new double[rows][columns];
        Random random = new Random();
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < columns; j++) {
                matrix[i][j] = random.nextDouble();
            }
        }
    }

    public void testOne(double[] input, int epochs) {
        this.input = input;
        this.loopCount = 0;
        long start = System.currentTimeMillis();
        long duration;
        for (int i = 0; i < epochs; i++) {
            multOne();
        }
        duration = System.currentTimeMillis() - start;
        System.out.println("Duration for testOne with " + epochs + " epochs: " + duration + ", loopCount: " + loopCount);
    }

    public void multOne() {
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < columns; j++) {
                arrayOne[i] += matrix[i][j] * arrayOne[i] * input[j];
                arrayTwo[i] += matrix[i][j] * arrayTwo[i] * input[j];
                arrayThree[i] += matrix[i][j] * arrayThree[i] * input[j];
                loopCount++;
            }
        }
    }

    public void testTwo(double[] input, int epochs) {

        this.loopCount = 0;
        long start = System.currentTimeMillis();
        long duration;
        for (int i = 0; i < epochs; i++) {
            arrayOne = multTwo(matrix, arrayOne, input);
            arrayTwo = multTwo(matrix, arrayTwo, input);
            arrayThree = multTwo(matrix, arrayThree, input);
        }
        duration = System.currentTimeMillis() - start;
        System.out.println("Duration for testTwo with " + epochs + " epochs: " + duration + ", loopCount: " + loopCount);
    }

    public double[] multTwo(double[][] matrix, double[] array, double[] input) {
        double[] newArray = new double[rows];
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < columns; j++) {
                newArray[i] += matrix[i][j] * array[i] * input[j];
                loopCount++;
            }
        }
        return newArray;
    }

    public static void main(String[] args) {
        int rows = 100;
        int columns = 128;
        ArrayTest arrayTest = new ArrayTest(rows, columns);
        Random random = new Random();
        double[] input = new double[columns];
        for (int i = 0; i < columns; i++) {
            input[i] = random.nextDouble();
        }
        arrayTest.testOne(input, 5000);
        arrayTest.testTwo(input, 5000);
    }
}
Run Code Online (Sandbox Code Playgroud)

pau*_*l-g 2

您的测试花费不同时间的原因很简单:它们不做相同的事情。由于您比较的两个循环在功能上并不相同,因此迭代次数并不是一个值得关注的指标。

testOne需要更长的时间,testTwo因为:

  • 在循环的每次迭代期间就地multOne更新。这意味着对于内部循环的每次迭代, 您都使用在上一次迭代中计算出的新值。这会创建循环携带依赖性,这对于编译器来说更难优化,因为您需要下一个 CPU 时钟周期的操作输出。这对于浮点运算来说实际上是不可能的,浮点运算通常有几个时钟周期的延迟,因此会导致停顿,从而降低性能。arrayOne[i]jjarrayOne[i]matrix[i][j] * arrayOne[i] * input[j]

  • 在每次迭代时仅testTwo更新一次,并且由于没有携带的依赖项,因此可以有效地矢量化循环,从而获得更好的缓存和算术性能。arrayOneepoch