2015-11-10 4 views
1

Просто расскажите о Java, и позвольте мне сказать это первым: я не беспокоюсь об этом из-за оптимизации производительности, просто любопытно, что за кулисами вообще. Так как я не нашел ничего этого я предполагаю, что они эквивалентны во всех отношениях, но только, чтобы быть уверенным:Вызов функции с i + 1, ++ i разницей реализации?

В рекурсивной функции foo(int i), или я думаю, в generel в вызове функции, есть разница между foo(++i) и foo(i + 1)? Я знаю, что результат тот же, но я думал, что с ++i может сделать некоторые дополнительные работы в фоновом режиме, потому что это назначение? Может быть, он создает (anonymous field) = i в фоновом режиме, который увеличивается, а затем ссылается? Или компилятор просто оптимизирует ++i от i + 1?

Мое понимание немного нечеткое, как вы видите, поэтому помощь будет оценена.

РЕДАКТИРОВАТЬ уточнить:

Сначала это было связано с рекурсивной функции, например,

public static int foo(int i) { 
     return (i >= 4) ? 0 
      : i + foo(++i); 
    } 

«Функции в целом» -часть пришли в середине написания этого вопроса и, как заметил, делает этот неоднозначный и т.д. Надеюсь, что это проясняет все.

+1

Я предлагаю вам использовать http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javap.html два простых класса (или класс с двумя методами), каждый с одним из в двух подходах вы лучше поймете все это, а не прочитаете здесь ответ. ^^ –

+1

Компилятор JIT действительно мог бы сделать такую ​​оптимизацию на том основании, что 'i' мертв после приращения. Действительно ли это делает такую ​​оптимизацию, это еще один вопрос. Компилятор Java-to-bytecode не оптимизирует это для вас; приращение будет в байт-коде. Это на 100% до компилятора JIT для оптимизации. –

+0

* «Я знаю, что результат тот же» * Зависит от кода. Если вы получаете доступ к «i» более одного раза, результат не совпадает. – Tom

ответ

2

Если ответ не о семантике, а о производительности на уровне машины после того, как ИК оптимизирован, переведен в машинные инструкции, я бы сказал: «нет, если только не измерено и не доказано иное».

Это очень маловероятно, что будет разница в производительности после всех оптимизаций между f(++i) и f(i+1), предполагая, что ваш код таким образом, что вы можете рассматривать их в качестве альтернативы (если предположить, что состояние i перестает стать актуальным после того, как вызов функции).

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

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

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

0

Если i используется после foo(++i), значение будет изменено

Если вы обрабатываете i после foo(i + 1), значение не было изменено

foo(int i) 
{ 
    //process stuff A 
    foo(i + 1); 
    //process stuff B 
} 

bar(int i) 
{ 
    //process stuff A 
    bar(i + 1); 
    //process stuff B 
} 

В зависимости от того, что находится в //process stuff B, вы могут либо хотеть использовать i + 1 и иметь значение i, либо ++i, и имеют i приратся

+0

Это не отвечает на вопрос OP, а именно, оптимизируется ли '++ i' на' i + 1' на том основании, что 'i' мертв после вызова. Конечно, ОП уже знает, что делает «++ i». –

+0

@ ChrisJester-Young это полностью зависит от того, что вызывается после рекурсивного цикла – phflack

+0

Конечно. Но вопрос OP сосредотачивается вокруг дела 'i'-is-dead-after-' ++ i'. –

2

Различия в исполнении foo(++i) и foo(i + 1): значение параметра, переданного в foo, не меняется.

Однако в первом случае локальная переменная i увеличивается на единицу; в последнем случае он сохраняет свое первоначальное значение.

Так что, если вы сделаете тот же метод вызова дважды, есть разница:

foo(++i); 
foo(++i); // Invoke with i 1 greater than before. 

отличается от

foo(i + 1); 
foo(i + 1); // Invoke with the same argument 

Как таковой, в общем, компилятор ничего не может оптимизировать прочь, потому что они имеют разную семантику.

+0

@ Downvoter: помогите объяснить? –

+0

Это не отвечает на вопрос OP, а именно, оптимизируется ли '++ i' на' i + 1' на том основании, что 'i' мертв после вызова. Конечно, ОП уже знает, что делает «++ i». –

+0

Кроме того, некоторое терпение, пожалуйста: я сначала опускаю голову и объясняю позже, а не наоборот. ;-) –

2

Рядом с другими комментариями о значении i после использования приращения префикса. В сгенерированном байт-коде есть небольшая разница. Ниже приведены упрощенные результаты.

Foo (++ я) компилируется

iinc 
iload_1 
invokestatic 

Foo (я + 1) компилируется

iload_1            
iconst_1            
iadd             
invokestatic 
+0

Байт-код, конечно, отличается. 'javac' вообще не оптимизирует генерируемый байт-код; он оставляет работу по оптимизации JIT-компилятору. Вопрос интереса заключается в том, действительно ли компилятор JIT оптимизирует прирост. –

1

да, я ++ состоит из двух операций сложения и assignment.Since, вы мне больше не нужно, i + 1 лучше использовать, а не ++ i.

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