Почему этот код с несколькими операциями "или" несколько быстрее, чем с помощью таблицы поиска в Java?

Рассматривая вопрос микро-оптимизации, который я вчера спросил (здесь), я нашел что-то странное: оператор or в Java работает немного быстрее, чем поиск булевого значения в массиве логических значений.

В моих тестах, используя приведенные ниже алгоритмы на значениях long от 0 до 1 миллиарда, alg1 примерно на 2% быстрее. (Я изменил порядок, в котором тестируются алгоритмы, и получаю те же результаты). Мой вопрос: Почему alg1 быстрее? Я бы ожидал, что alg2 будет немного быстрее, так как он использует таблицу поиска, тогда как alg1 должен выполнить 4 сравнения и 3 или операции для 75% входов.

private final static boolean alg1(long n)
{
  int h = (int)(n & 0xF);
  if(h == 0 || h == 1 || h == 4 || h == 9)
  {
    long tst = (long)Math.sqrt(n);
    return tst*tst == n;
  }  
  return false;

}

private final static boolean[] lookup = new boolean[16];
static
{
  lookup[0] = lookup[1] = lookup[4] = lookup[9] = true;
}
private final static boolean alg2(long n)
{
  if(lookup[(int)(n & 0xF)])
  {
    long tst = (long)Math.sqrt(n);
    return tst*tst == n;
  }
  else
    return false;
}

Если вам интересно, этот код проверяет, является ли число идеальным квадратом и использует тот факт, что идеальные квадраты должны заканчиваться на 0, 1, 4 или 9 в шестнадцатеричном формате.

Ответ 1

Загрузка некоторой случайной части данных, как правило, медленнее, чем небольшой не ветвящийся код.

Все зависит от архитектуры процессора, конечно. Ваш первый оператор if может быть реализован как четыре инструкции. Второй может потенциально нуждаться в проверке нулевого указателя, проверке границ, а также нагрузке и сравнении. Кроме того, больше кода означает больше времени компиляции и больше шансов на то, что оптимизация будет изменена каким-то образом.

Ответ 2

Я бы предположил, что проблема заключается в том, что проверка диапазона для массива и если поиск массива реализован как вызов метода. Это, безусловно, затмевает 4 прямых int сравнения. Вы посмотрели на байтовый код?

Ответ 3

Согласно эта статья, доступ к элементам массива "в 2 или 3 раза дороже, чем доступ к элементам без массива". Ваш тест показывает, что разница может быть даже больше.

Ответ 4

Это интересный фрагмент кода, но 2% - действительно небольшая разница. Я не думаю, что вы можете сделать вывод из этого.

Ответ 5

В текущем примере я согласен с тем, что проверка границ - это, вероятно, то, что вам нужно (почему JVM не оптимизирует это вне меня - образец кода может детерминистически отображаться, чтобы не переполнять...

Другая возможность (особенно с большими таблицами поиска) - это латентность кэша... Это зависит от размера регистров процессоров и того, как JVM выбирает их использовать, - но если массив байтов не поддерживается полностью на процессоре, то вы увидите повышение производительности по сравнению с простым OR, так как массив вытаскивается на CPU для каждой проверки.