Что такое метод инверсии цикла?

Я просматривал документ, в котором говорится о компилятор just-in-time (JIT) методы оптимизации для Java. Одним из них была "петлевая инверсия". И в документе говорится:

вы заменяете обычный while петли с do-while петли. И do-while цикл устанавливается в if предложения. Эта замена приводит к двум меньшим скачкам.

как работает инверсия цикла и как она оптимизирует наш код путь?

Б. Н.: было бы здорово, если бы кто-нибудь мог объяснить на примере кода Java и как JIT оптимизирует его для собственного кода и почему он оптимален в современных процессорах.

4 ответов


while (condition) { 
  ... 
}

документооборот:

  1. проверяем условие;
  2. если false, перейти к вне цикла;
  3. выполнить одну итерацию;
  4. перейти к верхней.

if (condition) do {
  ...
} while (condition);

документооборот:

  1. проверяем условие;
  2. если false, перейти за пределы цикла;
  3. выполнить одну итерацию;
  4. проверяем условие;
  5. если true, перейдите к шагу 3.

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

переходы на современные конвейерные архитектуры ЦП могут быть довольно дорогими: поскольку ЦП заканчивает выполнение проверок перед переходом, инструкции за этот прыжок уже в середине трубопровода. Вся эта обработка должна быть отброшена, если предсказание ветви не удается. Дальнейшее выполнение отложено, пока конвейер находится в стадии перепрофилирования.

объяснения указанных предсказания ветвлений: для каждого вида условного перехода CPU имеет два инструкции, каждая из которых включает ставки о результатах. Например, вы бы поставили инструкцию"переход если не ноль, ставки на не ноль " в конце цикла, потому что прыжок должен быть сделан на всех итерациях, кроме последней. Таким образом, CPU начинает перекачивать свой конвейер с инструкциями, следующими за целью прыжка, а не теми, которые следуют самой инструкции прыжка.

важное замечание

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


это может оптимизировать цикл, который всегда выполняется хотя бы один раз.

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

int i = 0;
while (i++ < 1) {
    //do something
}  

A do-while петля с другой стороны пропустит первый и последний прыжок. Вот эквивалентный цикл к приведенному выше, который будет работать без прыжков:

int i = 0;
if (i++ < 1) {
    do {
        //do something
    } while (i++ < 1); 
}

давайте пройдемся по ним:

на while версия:

void foo(int n) {
    while (n < 10) {
       use(n);
       ++n;
    }
    done();
}
  1. Сначала мы тестируем n и перейти к done(); если условие неверно.
  2. затем мы используем и прирастить n.
  3. теперь мы возвращаемся к состоянию.
  4. смыть, повторить.
  5. когда условие больше не истинно, мы переходим к done().

на do-while версия:

(помните, мы на самом деле не делайте этого в исходном коде [это приведет к проблемам обслуживания], компилятор/JIT делает это за нас.)

void foo(int n) {
    if (n < 10) {
        do {
            use(n);
            ++n;
        }
        while (n < 10);
    }
    done();
}
  1. Сначала мы тестируем n и перейти к done(); если условие неверно.
  2. затем мы используем и прирастить n.
  3. теперь мы проверяем условие и прыгаем назад, если это правда.
  4. смыть, повторить.
  5. когда условие больше не истинно, мы пропускаем (не прыгаем) к done().

так, например, если n начинает быть 9, мы никогда не прыгаем вообще в do-while версия, а в while версия мы должны вернуться к началу, сделать тест, а потом вернуться к концу, когда мы видим, что это неправда.


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

этой ссылке предоставляет еще один пример инверсии цикла. В нескольких архитектурах, где decrement и compare реализованы как один набор команд, имеет смысл преобразовать цикл for В while с decrement и compare операция.

Википедия имеет очень хороший пример, и я объясняю это здесь снова.

 int i, a[100];
  i = 0;
  while (i < 100) {
    a[i] = 0;
    i++;
  }

будет преобразован компилятором в

  int i, a[100];
  i = 0;
  if (i < 100) {
    do {
      a[i] = 0;
      i++;
    } while (i < 100);
  }

как это переводится на производительность? Когда значение i равно 99, процессору не нужно выполнять GOTO (что требуется в первом случае). Это повышает производительность.