2014-01-08 2 views
0

Возьмем, например, цикл, как это:Может ли компилятор оптимизировать создание ссылок?

public boolean method(){ 
    for (int i = 0; i < 5; i++) { 
     if (this.object.getSomething().getSomeArray().get(i).getArray().size() > 0) 
      return false; 
    } 
    return true; 
} 

Каждый get метод просто возвращает частный атрибут. Более читаемый вариант того же кода будет:

public boolean method(){ 
    MySomeArray mySomeArray = this.object.getSomething().getSomeArray(); 
    for (int i = 0; i < 5; i++) { 
     MyArray array = mySomeArray.get(i).getArray(); 
     if (array.size() > 0) 
      return false; 
    } 
    return true; 
} 

Другая версия:

public boolean method(){ 
    MySomeArray mySomeArray = this.object.getSomething().getSomeArray(); 
    MyArray array; 
    for (int i = 0; i < 5; i++) { 
     array = mySomeArray.get(i).getArray(); 
     if (array.size() > 0) 
      return false; 
    } 
    return true; 
} 

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

+0

FWIW, вторая и третья версии практически полностью эквивалентны, возможно, даже с точки зрения сгенерированной сборки. – BeeOnRope

+0

@BeeOnRope - это не проблема в декларации внутри цикла? Другими словами, должен ли я всегда свободно объявлять переменные внутри цикла? – HAL9000

+0

Да, вы должны чувствовать себя свободно. В месте объявления нет такой вещи, как «создание ссылок». Объявление самих местных жителей бесплатное - только когда вы их назначаете (например, через new() или вызываете какой-либо метод) имеет любую стоимость (даже на уровне байт-кода, даже не учитывая JIT). Таким образом, нет недостатка в объявлении переменной в цикле, и на самом деле считается хорошей практикой объявлять переменную в наименьшей возможной области. – BeeOnRope

ответ

2

Если MySomeArray, а также все другие классы, участвующие в цепочке разыменования, находятся в нижней части их соответствующих иерархии классов, то HotSpot будет легко перевести все эти вызовы виртуальных функций в «простые» (не виртуальные)) вызывает метод, известный как сайт мономорфного вызова оптимизация.

Это может произойти, даже если классы не являются классами листьев. Важно то, что на каждом сайте вызова отправляется только один тип объекта.

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

Обратите внимание, что большая часть выше подлежит весь код пути быть свободным от любого происходит, перед тем отношения к действиям других потоков , На практике это в основном означает отсутствие переменного доступа и без synchronized блоков (в пределах вашего собственного кода, а также всего кода, вызванного вашим кодом).

+0

Очень полезно, спасибо, мне нужно улучшить свои знания об этой теме. – HAL9000

0

Проблема в том, что вы делаете предположения, которые компилятор не может сделать.

Вы знаете, что this.object.getSomething().getSomeArray() не меняется каждый раз вокруг цикла, но компилятор не знает об этом. Тем более, что другие потоки могут потенциально изменять эти переменные одновременно ...

+1

Вы недооцениваете полномочия HotSpot ... другие потоки не имеют отношения к делу, если нет явного указания * происходит до * заказа. –

1

Напишите тестовый пример, который использует этот метод, и print the generated assembly code при его запуске. Затем вы можете проверить, сколько звонков включено. Я скептически отношусь к тому, что компилятор способен объединить их все, но компилятор JIT может удивить.

Я бы предпочел более читаемую версию в любом случае, потому что она более читаема.

+0

Это хороший совет, я попробую. – HAL9000

1

С достаточным вложением, компилятор может действительно Подъемник Метод вызывается из цикла, очень похожий на то, что вы делали вручную во втором и третьем примерах. Детали того, будет ли это на самом деле делать это, полностью зависят от поведения и размера рассматриваемых методов, а также от сложности JIT.

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

+0

HotSpot может легко обнаруживать * мономорфные сайты вызовов * и перейти оттуда к встраиванию, подъему и т. Д. –

+0

Я согласен, что сайты с мономорфными вызовами легко обнаруживаются, но по какой-либо причине с нелокальными ссылками сборка обычно показывает перечитание (из памяти) ссылки, а не подъема, разворота и т. д. Я в основном смотрел на разборку Java 6, так что с тех пор она улучшилась. – BeeOnRope

+0

Я неоднократно встречался с поднятием вызовов методов в контексте цикла (например, 'String # length'). –

Смежные вопросы