Почему обращение цикла делает его медленнее?

У меня есть следующий код, который выполняет циклический сдвиг битов в массиве:

private static void method1(byte[] bytes) {
    byte previousByte = bytes[0];
    bytes[0] = (byte) (((bytes[0] & 0xff) >> 1) | ((bytes[bytes.length - 1] & 0xff) << 7));
    for (int i = 1; i < bytes.length; i++) {
       byte tmp = bytes[i];
       bytes[i] = (byte) (((bytes[i] & 0xff) >> 1) | ((previousByte & 0xff) << 7));
       previousByte = tmp;
    }
}

Тогда я подумал, что это проще и читабельнее чтобы вернуться назад следующим образом:

private static void method2(byte[] bytes) {
    byte lastByte = bytes[bytes.length-1];
    for (int i = bytes.length-1; i > 0; i--) {
       bytes[i] = (byte) (((bytes[i] & 0xff) >> 1) | ((bytes[i-1] & 0xff) << 7));
    }
    bytes[0] = (byte) (((bytes[0] & 0xff) >> 1) | ((lastByte & 0xff) << 7));
}

Но я заметил, что второй (метод2) медленнее, чем первый (метод1)! Я заметил разницу, потому что я вызываю метод тысячи раз. Поэтому я сделал тест на убедитесь, и вот средний результат из 20 тестов с вызовом каждого метода 3000 раз (и количество байтов 1 миллион):

method1 average : 4s 572ms
method2 average : 5s 630ms

Итак, мой вопрос: почему первый быстрее, чем второй?

Вот это код тестирования, чтобы убедиться, что я не делаю что-то неправильно при тестировании:

import java.math.BigInteger;

public class BitShiftTests {

public static void main(String[] args) {

    int numOfTests = 20;
    int numberOfShifts = 3000;
    byte[] numbers = new byte[1000000];
    for (int i = 0; i < numbers.length; i++) {
        numbers[i] = (byte) (i % 255);
    }

    System.out.println("Testing method1...");
    BigInteger method1Sum = new BigInteger("00000000", 2);
    for (int i = 1; i <= numOfTests; i++) {
       long total = 0L;
       for (int j = 0; j < numberOfShifts; j++) {
          long startTime = System.nanoTime();
          method1(numbers);
          long endTime   = System.nanoTime();
          total = total + (endTime - startTime);
       }
       method1Sum = method1Sum.add(new BigInteger(Long.toString(total), 10));
       System.out.println(String.format("%-2d: %s", i, getTime(total)));
    }

    System.out.println("Testing method2...");
    BigInteger method2Sum = new BigInteger("00000000", 2);
    for (int i = 1; i <= numOfTests; i++) {
       long total = 0L;
       for (int j = 0; j < numberOfShifts; j++) {
          long startTime = System.nanoTime();
          method2(numbers);
          long endTime   = System.nanoTime();
          total = total + (endTime - startTime);
       }
       method2Sum = method2Sum.add(new BigInteger(Long.toString(total), 10));
       System.out.println(String.format("%-2d: %s", i, getTime(total)));
    }

    System.out.println("method1 average :   " + getTime(method1Sum.longValue() / numOfTests));
    System.out.println("method2 average :   " + getTime(method2Sum.longValue() / numOfTests));
}

private static void method1(byte[] bytes) {
    byte previousByte = bytes[0];
    bytes[0] = (byte) (((bytes[0] & 0xff) >> 1) | ((bytes[bytes.length - 1] & 0xff) << 7));
    for (int i = 1; i < bytes.length; i++) {
       byte tmp = bytes[i];
       bytes[i] = (byte) (((bytes[i] & 0xff) >> 1) | ((previousByte & 0xff) << 7));
       previousByte = tmp;
    }
}

private static void method2(byte[] bytes) {
    byte lastByte = bytes[bytes.length-1];
    for (int i = bytes.length-1; i > 0; i--) {
       bytes[i] = (byte) (((bytes[i] & 0xff) >> 1) | ((bytes[i-1] & 0xff) << 7));
    }
    bytes[0] = (byte) (((bytes[0] & 0xff) >> 1) | ((lastByte & 0xff) << 7));
}

private static String getTime(long nanoSecs) {

  int minutes = (int) (nanoSecs / 60000000000.0);
  int seconds = (int) (nanoSecs / 1000000000.0) - (minutes * 60);
  int millisecs = (int) (((nanoSecs / 1000000000.0) - (seconds + minutes * 60)) * 1000);
  int nanosecs = (int) nanoSecs - (millisecs * 1000000000);

  if (minutes == 0 && seconds == 0 && millisecs == 0) {
     return nanosecs + "ns";
  }

  if (minutes == 0 && seconds == 0) {
     return millisecs + "ms";
  }

  if (minutes == 0 && millisecs == 0) {
     return seconds + "s";
  }

  if (seconds == 0 && millisecs == 0) {
     return minutes + "min";
  }

  if (minutes == 0) {
     return seconds + "s " + millisecs + "ms";
  }

  if (seconds == 0) {
     return minutes + "min " + millisecs + "ms";
  }

  if (millisecs == 0) {
     return minutes + "min " + seconds + "s";
  }

  return minutes + "min " + seconds + "s " + millisecs + "ms";
}
}

Обновление:

Похоже, причина в том, что я обращаюсь к 2 различным индексам в каждом цикле во втором методе, пока я обращался только 1 индекс в первом методе, так что это не имеет ничего общего с обращением цикла

Спасибо @rm5248 и @Ben, я бы выбрал оба ваших ответа, если бы мог, но я выбрал более ранний один.

8
задан Motasim 2 December 2015 в 19:58
поделиться