Понимание производительности петель в jvm
Я играю с jmh
, а в разделе looping они сказали, что
Вы можете заметить, что чем больше количество повторений, тем ниже "воспринимаемая" стоимость измеряемой операции. До такой степени мы делать каждое дополнение с 1/20 нс, намного больше, чем оборудование может на самом деле делать. Это происходит потому, что цикл сильно разворачивается/конвейерно, и измеряемая операция поднимается из цикла. Мораль: не overuse loops, полагайтесь на JMH, чтобы получить правильное измерение.
Я сам это пробовал
@Benchmark
@OperationsPerInvocation(1)
public int measurewrong_1() {
return reps(1);
}
@Benchmark
@OperationsPerInvocation(1000)
public int measurewrong_1000() {
return reps(1000);
}
и получил следующий результат:
Benchmark Mode Cnt Score Error Units
MyBenchmark.measurewrong_1 avgt 15 2.425 ± 0.137 ns/op
MyBenchmark.measurewrong_1000 avgt 15 0.036 ± 0.001 ns/op
Это действительно показывает, что MyBenchmark.measurewrong_1000
значительно быстрее, чем MyBenchmark.measurewrong_1
. Но я не могу понять оптимизацию JVM, чтобы сделать это улучшение производительности.
Что они означают, что цикл разворачивается/конвейерно?
Ответы
Ответ 1
Развертывание петли делает возможной конвейерную обработку. Таким образом, процессор, пригодный для работы в трубопроводе (например, RISC), может параллельно выполнять развернутый код.
Итак, если ваш процессор способен параллельно выполнять 5 конвейеров, ваш цикл будет разворачиваться следующим образом:
// pseudo code
int pipelines = 5;
for(int i = 0; i < length; i += pipelines){
s += (x + y);
s += (x + y);
s += (x + y);
s += (x + y);
s += (x + y);
}
![Risc конвейер]()
IF = инструкция Fetch, ID = декодирование команды, EX = выполнение, MEM = доступ к памяти, WB = запись записи назад
Из Oracle White paper:
... стандартная оптимизация компилятора, которая позволяет ускорить выполнение цикла. Развертка цикла увеличивает размер тела петли, одновременно уменьшая количество итераций. Развертка цикла также повышает эффективность других оптимизаций.
Дополнительная информация о конвейерной обработке: Классический конвейер RISC
Ответ 2
Развертка Loop - это tecnhique для сглаживания итераций нескольких циклов, повторяя тело цикла.
Например. в данном примере
for (int i = 0; i < reps; i++) {
s += (x + y);
}
может быть развернут JIT-компилятором к чему-то вроде
for (int i = 0; i < reps - 15; i += 16) {
s += (x + y);
s += (x + y);
// ... 16 times ...
s += (x + y);
}
Затем тело расширенного цикла можно дополнительно оптимизировать до
for (int i = 0; i < reps - 15; i += 16) {
s += 16 * (x + y);
}
Очевидно, что вычисление 16 * (x + y)
намного быстрее, чем вычисление (x + y)
16 раз.
Ответ 3
Loop Pipelining = Консолидация программного обеспечения.
В принципе, это метод, который используется для оптимизации эффективности повторяющихся циклов последовательных, путем выполнения некоторых инструкций в теле цикла - в parrallel.
Конечно, это можно сделать только при выполнении определенных условий, таких как каждая итерация не зависит от другого и т.д.
From insidehpc.com:
Программная конвейерная обработка, которая действительно не имеет ничего общего с аппаратной конвейерной обработкой, представляет собой метод оптимизации цикла для создания операторов в пределах итерации, независимой друг от друга. Цель состоит в том, чтобы удалить зависимости так, чтобы казалось бы последовательные инструкции могут выполняться параллельно.
Подробнее здесь: